package jp.riken.brain.ni.samuraigraph.base;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * A figure in which data, axes and other various objects are drawn.
 */
public abstract class SGFigure implements ActionListener, SGIConstants,
		SGIUndoable, SGIMovable, SGISelectable, SGIPaintable,
		SGIFigureDialogObserver, SGINode, SGIRootObjectConstants, SGIVisible,
		SGIDisposable, SGIFigureConstants {
	// ID-number of this figure.
	private int mID;

	/**
	 * A window which this figure belongs to.
	 */
	protected SGDrawingWindow mWnd = null;

	/**
	 * List of data objects.
	 */
	protected List mDataList = new ArrayList();

	/**
	 * X-coordinate of the graph rectangle in the client area.
	 */
	protected float mGraphRectX = 0.0f;

	/**
	 * Y-coordinate of the graph rectangle in the client area.
	 */
	protected float mGraphRectY = 0.0f;

	/**
	 * Width of the graph rectangle.
	 */
	protected float mGraphRectWidth = 0.0f;

	/**
	 * Height of the graph rectangle.
	 */
	protected float mGraphRectHeight = 0.0f;

	/**
	 * The magnification.
	 */
	protected float mMagnification = 1.0f;

	/**
	 * A pop-up menu.
	 */
	protected JPopupMenu mPopupMenu = new JPopupMenu();

	/**
	 * The property dialog of this figure.
	 */
	protected SGPropertyDialog mDialog = null;

	/**
	 * 
	 */
	private SGIFigureElement mPressedElement = null;

	/**
	 * 
	 */
	private Point mPressedPoint = new Point();

	/**
	 * 
	 */
	private final Rectangle2D mTempFigureRect = new Rectangle2D.Float();

	/**
	 * 
	 */
	private Color mBackgroundColor;

	/**
	 * A rectangle for mouse drag.
	 */
	private final Rectangle2D mDraggingRect = new Rectangle2D.Float();

	/**
	 * Whether to snap to grid.
	 */
	private static boolean mSnapToGridFlag = DEFAULT_FIGURE_SNAP_TO_GRID;

	/**
	 * A rectangle for the rubber band.
	 */
	private final Rectangle2D mRubberBandRect = new Rectangle2D.Float();

	/**
	 * Whether to use rubber band on dragging the figures.
	 */
	static boolean mRubberBandFlag = DEFAULT_FIGURE_RUBBER_BANDING_ENABLED;

	/**
	 * Whether to show rubber band on dragging the figures.
	 */
	static boolean mRubberBandVisibleFlag = false;

	/**
	 * location of mouse pointer
	 */
	private int mMouseLocation = 0;

	/**
	 * 
	 */
	protected static final float MIN_WIDTH = 50.0f;

	/**
	 * 
	 */
	protected static final float MAX_WIDTH = 50.0f;

	/**
	 * 
	 */
	private static boolean mBoundingBoxVisibleFlag = DEFAULT_FIGURE_BOUNDING_BOX_VISIBLE;

	/**
	 * 
	 */
	protected SGProperties mTemporaryProperties = null;

	/**
	 * 
	 */
	private boolean mTransparentFlag = false;

	public static final String MENUCMD_SHOW_BOUNDING_BOX = "Show Bounding Box";

	/**
	 * Create a figure which belongs to the given window.
	 * 
	 * @param wnd
	 *            ths window this figure belongs
	 */
	public SGFigure(final SGDrawingWindow wnd) {
		super();
		// this.setWindow(wnd);
		this.mWnd = wnd;
		this.mComponent = wnd.getFigurePanel();
		// this.setComponent( wnd.getFigurePanel() );
		this.create();
	}

	/**
	 * 
	 * @return
	 */
	public String toString() {
		return new String("SGFigure:" + this.getID());
	}

	/**
	 * 
	 * @return
	 */
	private boolean create() {
		this.createDialog();
		this.createPopupMenu();

		// initialize properties
		this.setBackgroundColor(DEFAULT_FIGURE_BACKGROUND_COLOR);
		this.setVisible(true);
		// this.setOpaque(false);

		return true;
	}

	private boolean mVisibleFlag = true;

	public boolean isVisible() {
		return this.mVisibleFlag;
	}

	public void setVisible(final boolean b) {
		this.mVisibleFlag = b;
	}

	private JComponent mComponent = null;

	public JComponent getComponent() {
		return this.mComponent;
	}

	// public void setComponent( JComponent com )
	// {
	// this.mComponent = com;
	// }

	public void repaint() {
		// System.out.println("Figure");
		this.mComponent.repaint();
	}

	public int getWidth() {
		return this.mComponent.getWidth();
	}

	public int getHeight() {
		return this.mComponent.getHeight();
	}

	//
	// setters and getters
	//

	/**
	 * 
	 */
	public int getID() {
		return this.mID;
	}

	/**
	 * 
	 */
	public boolean setID(final int id) {
		this.mID = id;
		return true;
	}

	/**
	 * 
	 */
	public boolean setBackgroundColor(final Color color) {
		this.mBackgroundColor = color;
		return true;
	}

	/**
	 * 
	 */
	public boolean setBackgroundColor(final String value) {
		final Color cl = SGUtilityText.getColor(value);
		if (cl == null) {
			return false;
		}
		return this.setBackgroundColor(cl);
	}

	/**
	 * 
	 * @param r
	 * @param g
	 * @param b
	 * @return
	 */
	public boolean setBackgroundColor(final String r, final String g,
			final String b) {
		final Color cl = SGUtilityText.getColor(r, g, b);
		if (cl == null) {
			return false;
		}
		return this.setBackgroundColor(cl);
	}

	/**
	 * 
	 */
	public Color getBackgroundColor() {
		return this.mBackgroundColor;
	}

	/**
	 * 
	 */
	public boolean isTransparent() {
		return this.mTransparentFlag;
	}

	/**
	 * 
	 */
	public SGDrawingWindow getWindow() {
		return this.mWnd;
	}

	/**
	 * 
	 * @return
	 */
	public float getSpaceAxisLineAndNumber() {
		return this.getAxisElement().getSpaceAxisLineAndNumber();
	}

	/**
	 * 
	 */
	public float getSpaceAxisLineAndNumber(final String unit) {
		final float space = this.getSpaceAxisLineAndNumber();
		return (float) SGUtilityText.convertFromPoint(space, unit);
	}

	/**
	 * 
	 * @return
	 */
	public float getSpaceNumberAndTitle() {
		return this.getAxisElement().getSpaceNumberAndTitle();
	}

	/**
	 * 
	 */
	public float getSpaceNumberAndTitle(final String unit) {
		final float space = this.getSpaceNumberAndTitle();
		return (float) SGUtilityText.convertFromPoint(space, unit);
	}

	/**
	 * 
	 * @return
	 */
	public boolean setSpaceAxisLineAndNumber(final float space) {
		final SGIFigureElementAxis aElement = this.getAxisElement();
		final float s = aElement.getSpaceAxisLineAndNumber();
		if (space != s) {
			aElement.setSpaceAxisLineAndNumber(space);
			aElement.setChanged(true);
			this.updateGraphRect();
		}
		return true;
	}

	/**
	 * 
	 * @param value
	 */
	public boolean setSpaceAxisLineAndNumber(final float value,
			final String unit) {
		final double conv = SGUtilityText.convert(value, unit,
				FIGURE_SPACE_UNIT);
		if (conv < FIGURE_SPACE_TO_SCALE_MIN)
			return false;
		if (conv > FIGURE_SPACE_TO_SCALE_MAX)
			return false;

		return this.setSpaceAxisLineAndNumber((float) SGUtilityText
				.convertToPoint(value, unit));
	}

	/**
	 * 
	 * @return
	 */
	public boolean setSpaceNumberAndTitle(final float space) {
		final SGIFigureElementAxis aElement = this.getAxisElement();
		final float s = aElement.getSpaceNumberAndTitle();
		if (space != s) {
			aElement.setSpaceNumberAndTitle(space);
			aElement.setChanged(true);
			this.updateGraphRect();
		}
		return true;
	}

	/**
	 * 
	 * @param value
	 */
	public boolean setSpaceNumberAndTitle(final float value, final String unit) {
		final double conv = SGUtilityText.convert(value, unit,
				FIGURE_SPACE_UNIT);
		if (conv < FIGURE_SPACE_TO_TITLE_MIN)
			return false;
		if (conv > FIGURE_SPACE_TO_TITLE_MAX)
			return false;

		return this.setSpaceNumberAndTitle((float) SGUtilityText
				.convertToPoint(value, unit));
	}

	/**
	 * 
	 */
	public boolean setLegendVisible(boolean flag) {
		final SGIFigureElementLegend lElement = this.getLegendElement();
		final boolean v = lElement.isLegendVisible();
		if (v != flag) {
			lElement.setLegendVisible(flag);
			lElement.setChanged(true);
		}
		return true;
	}

	/**
	 * 
	 */
	public boolean setTransparent(boolean flag) {
		this.mTransparentFlag = flag;
		return true;
	}

	// /**
	// *
	// * @param wnd
	// * @return
	// */
	// public void setWindow( final SGDrawingWindow wnd )
	// {
	// this.mWnd = wnd;
	// this.setComponent( wnd.getFigurePanel() );
	// }

	// Map of figure elements.
	private final Map mFigureElementMap = new TreeMap();

	/**
	 * Get the array of SGIFigureElement objects.
	 * 
	 * @return the array of SGIFigureElement objects
	 */
	public SGIFigureElement[] getIFigureElementArray() {
		ArrayList list = new ArrayList(this.mFigureElementMap.values());
		SGIFigureElement[] array = (SGIFigureElement[]) list
				.toArray(new SGIFigureElement[] {});
		return array;
	}

	/**
	 * Returns a list of child nodes.
	 * 
	 * @return a list of chid nodes
	 */
	public ArrayList getChildNodes() {
		return new ArrayList(this.mFigureElementMap.values());
	}

	/**
	 * 
	 * @return
	 */
	public String getClassDescription() {
		return "Figure: " + this.getID();
	}

	/**
	 * 
	 * @return
	 */
	public String getInstanceDescription() {
		final float ratio = SGIConstants.CM_POINT_RATIO;
		final int order = SGIRootObjectConstants.MINIMAL_LENGTH_ORDER;
		final float x = (float) SGUtilityNumber.roundOffNumber(this.mGraphRectX
				* ratio, order - 1);
		final float y = (float) SGUtilityNumber.roundOffNumber(this.mGraphRectY
				* ratio, order - 1);

		String str = this.getClassDescription();
		str += " ( X=" + x + "cm, Y=" + y + "cm )";

		return str;
	}

	/**
	 * 
	 * @param el
	 * @return
	 */
	protected SGIFigureElement getIFigureElement(final Class cl) {
		if (cl == null) {
			throw new IllegalArgumentException("");
		}

		final SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			if (cl.isAssignableFrom(array[ii].getClass())) {
				return array[ii];
			}
		}
		return null;
	}

	/**
	 * 
	 * @return
	 */
	public SGIFigureElementAxis getAxisElement() {
		return (SGIFigureElementAxis) this
				.getIFigureElement(SGIFigureElementAxis.class);
	}

	/**
	 * 
	 * @return
	 */
	public SGIFigureElementGraph getGraphElement() {
		return (SGIFigureElementGraph) this
				.getIFigureElement(SGIFigureElementGraph.class);
	}

	/**
	 * 
	 * @return
	 */
	public SGIFigureElementLegend getLegendElement() {
		return (SGIFigureElementLegend) this
				.getIFigureElement(SGIFigureElementLegend.class);
	}

	/**
	 * 
	 * @param f
	 */
	public void setIFigureElement(final int layer, final SGIFigureElement f) {
		if (f == null) {
			throw new IllegalArgumentException("");
		}

		final Class cl = f.getClass();
		Iterator itr = this.mFigureElementMap.values().iterator();
		while (itr.hasNext()) {
			final SGIFigureElement ff = (SGIFigureElement) itr.next();
			if (ff.getClass().equals(cl)) {
				throw new IllegalArgumentException("");
			}
		}

		this.mFigureElementMap.put(new Integer(layer), f);
	}

	/**
	 * 
	 * @param el
	 */
	void clearFocusedObjectsOtherThan(SGIFigureElement el) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			if (array[ii].getClass().equals(el.getClass()) == false) {
				array[ii].clearFocusedObjects();
			}
		}
		this.setSelected(false);
	}

	/**
	 * 
	 */
	public boolean isGraphRectContains(Point2D point) {
		final Rectangle2D rect = this.getGraphRect();
		return rect.contains(point);
	}

	/**
	 * 
	 */
	public boolean isGraphRectContains(int x, int y) {
		final Rectangle2D rect = this.getGraphRect();
		return rect.contains(x, y);
	}

	/**
	 * 
	 */
	public Rectangle2D getGraphRect() {
		final Rectangle2D rect = new Rectangle2D.Float(this.getGraphRectX(),
				this.getGraphRectY(), this.getGraphRectWidth(), this
						.getGraphRectHeight());
		return rect;
	}

	public Rectangle2D getGraphRectInClientRect() {
		final Rectangle2D rect = new Rectangle2D.Float(this.mGraphRectX,
				this.mGraphRectY, this.mGraphRectWidth, this.mGraphRectHeight);
		return rect;
	}

	/**
	 * 
	 * @return
	 */
	public float getGraphRectX() {
		final Rectangle2D cRect = this.mWnd.getPaperRect();
		return (float) cRect.getX() + this.mMagnification * this.mGraphRectX;
	}

	/**
	 * 
	 * @return
	 */
	public float getGraphRectY() {
		final Rectangle2D cRect = this.mWnd.getPaperRect();
		return (float) cRect.getY() + this.mMagnification * this.mGraphRectY;
	}

	/**
	 * 
	 * @return
	 */
	public float getGraphRectWidth() {
		return this.mMagnification * this.mGraphRectWidth;
	}

	/**
	 * 
	 * @return
	 */
	public float getGraphRectHeight() {
		return this.mMagnification * this.mGraphRectHeight;
	}

	/**
	 * 
	 */
	public float getFigureX() {
		return this.mGraphRectX;
	}

	/**
	 * 
	 */
	public float getFigureX(final String unit) {
		return (float) SGUtilityText.convertFromPoint(this.getFigureX(), unit);
	}

	/**
	 * 
	 */
	public float getFigureY() {
		return this.mGraphRectY;
	}

	/**
	 * 
	 */
	public float getFigureY(final String unit) {
		return (float) SGUtilityText.convertFromPoint(this.getFigureY(), unit);
	}

	/**
	 * 
	 */
	public float getFigureWidth() {
		return this.mGraphRectWidth;
	}

	/**
	 * 
	 */
	public float getFigureWidth(final String unit) {
		return (float) SGUtilityText.convertFromPoint(this.getFigureWidth(),
				unit);
	}

	/**
	 * 
	 */
	public float getFigureHeight() {
		return this.mGraphRectHeight;
	}

	/**
	 * 
	 */
	public float getFigureHeight(final String unit) {
		return (float) SGUtilityText.convertFromPoint(this.getFigureHeight(),
				unit);
	}

	/**
	 * 
	 * @param value
	 */
	public boolean setFigureX(final float value) {
		if (this.equalLength(this.mGraphRectX, value)) {
			return true;
		}
		this.mGraphRectX = value;
		this.updateGraphRect();
		return true;
	}

	/**
	 * 
	 * @param value
	 */
	public boolean setFigureX(final float value, final String unit) {
		final double conv = SGUtilityText.convert(value, unit,
				FIGURE_LOCATION_UNIT);
		if (conv < FIGURE_X_MIN)
			return false;
		if (conv > FIGURE_X_MAX)
			return false;

		return this.setFigureX((float) SGUtilityText
				.convertToPoint(value, unit));
	}

	/**
	 * 
	 * @param value
	 */
	public boolean setFigureY(final float value) {
		if (this.equalLength(this.mGraphRectY, value)) {
			return true;
		}
		this.mGraphRectY = value;
		this.updateGraphRect();
		return true;
	}

	/**
	 * 
	 * @param value
	 */
	public boolean setFigureY(final float value, final String unit) {
		final double conv = SGUtilityText.convert(value, unit,
				FIGURE_LOCATION_UNIT);
		if (conv < FIGURE_Y_MIN)
			return false;
		if (conv > FIGURE_Y_MAX)
			return false;

		return this.setFigureY((float) SGUtilityText
				.convertToPoint(value, unit));
	}

	/**
	 * 
	 * @param value
	 */
	public boolean setFigureWidth(final float value) {
		if (this.equalLength(this.mGraphRectWidth, value)) {
			return true;
		}
		this.mGraphRectWidth = value;
		this.updateGraphRect();
		return true;
	}

	/**
	 * 
	 * @param value
	 */
	public boolean setFigureWidth(final float value, final String unit) {
		final double conv = SGUtilityText
				.convert(value, unit, FIGURE_SIZE_UNIT);
		if (conv < FIGURE_WIDTH_MIN)
			return false;
		if (conv > FIGURE_WIDTH_MAX)
			return false;

		return this.setFigureWidth((float) SGUtilityText.convertToPoint(value,
				unit));
	}

	/**
	 * 
	 * @param value
	 */
	public boolean setFigureHeight(final float value) {
		if (this.equalLength(this.mGraphRectHeight, value)) {
			return true;
		}
		this.mGraphRectHeight = value;
		this.updateGraphRect();
		return true;
	}

	/**
	 * 
	 * @param value
	 */
	public boolean setFigureHeight(final float value, final String unit) {
		final double conv = SGUtilityText
				.convert(value, unit, FIGURE_SIZE_UNIT);
		if (conv < FIGURE_HEIGHT_MIN)
			return false;
		if (conv > FIGURE_HEIGHT_MAX)
			return false;

		return this.setFigureHeight((float) SGUtilityText.convertToPoint(value,
				unit));
	}

	private boolean equalLength(final float v1, final float v2) {
		final float diff = Math.abs(v1 - v2) * SGIConstants.CM_POINT_RATIO;
		final float ten = (float) SGUtilityNumber
				.getPowersOfTen(SGIRootObjectConstants.MINIMAL_LENGTH_ORDER);

		return (diff < ten);
	}

	/**
	 * 引数はピクセル単位の座標値
	 */
	public boolean setGraphRectLocation(final float x, final float y) {
		this.setGraphRectLocationAttributes(x, y);
		this.updateGraphRect();
		return true;
	}

	/**
	 * 引数はピクセル単位の座標値
	 */
	private boolean setGraphRectLocationAttributes(final float x, final float y) {
		final Rectangle2D cRect = this.mWnd.getPaperRect();
		this.mGraphRectX = (x - (float) cRect.getX()) / this.mMagnification;
		this.mGraphRectY = (y - (float) cRect.getY()) / this.mMagnification;
		return true;
	}

	/**
	 * 
	 * @param widthPt
	 * @param heightPt
	 * @return
	 */
	protected boolean setGraphRectLocationRoundingOut(final float xPt,
			final float yPt) {
		final Rectangle2D cRect = this.mWnd.getPaperRect();

		final float xCM = (xPt - (float) cRect.getX())
				* SGIConstants.CM_POINT_RATIO / this.mMagnification;
		final float yCM = (yPt - (float) cRect.getY())
				* SGIConstants.CM_POINT_RATIO / this.mMagnification;

		// round out the size
		final float dXCM = (float) SGUtilityNumber.roundOutNumber(xCM, -2);
		final float dYCM = (float) SGUtilityNumber.roundOutNumber(yCM, -2);

		// length in units of pixel
		final float x = dXCM / SGIConstants.CM_POINT_RATIO;
		final float y = dYCM / SGIConstants.CM_POINT_RATIO;

		//
		this.mGraphRectX = x;
		this.mGraphRectY = y;

		this.updateGraphRect();

		return true;
	}

	/**
	 * 
	 */
	public boolean setGraphRectSize(final float w, final float h) {
		this.setGraphRectSizeAttributes(w, h);
		this.updateGraphRect();
		return true;
	}

	/**
	 * 
	 */
	private boolean setGraphRectSizeAttributes(final float w, final float h) {
		this.mGraphRectWidth = w / this.mMagnification;
		this.mGraphRectHeight = h / this.mMagnification;
		return true;
	}

	/**
	 * 
	 * @param widthPt
	 * @param heightPt
	 * @return
	 */
	protected boolean setGraphRectSizeRoundingOut(final float widthPt,
			final float heightPt) {

		final float widthCM = widthPt * SGIConstants.CM_POINT_RATIO
				/ this.mMagnification;
		final float heightCM = heightPt * SGIConstants.CM_POINT_RATIO
				/ this.mMagnification;

		// round out the size
		final float dWidthCM = (float) SGUtilityNumber.roundOutNumber(widthCM,
				-2);
		final float dHeightCM = (float) SGUtilityNumber.roundOutNumber(
				heightCM, -2);

		// length in units of pixel
		final float width = dWidthCM / SGIConstants.CM_POINT_RATIO;
		final float height = dHeightCM / SGIConstants.CM_POINT_RATIO;

		//
		this.mGraphRectWidth = width;
		this.mGraphRectHeight = height;

		return true;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @return
	 */
	public boolean setGraphRect(final float x, final float y, final float w,
			final float h) {
		this.setGraphRectLocationAttributes(x, y);
		this.setGraphRectSizeAttributes(w, h);
		this.updateGraphRect();
		return true;
	}

	/**
	 * 
	 */
	public boolean isLegendVisible() {
		return this.getLegendElement().isLegendVisible();
	}

	/**
	 * Add a new data.
	 * 
	 * @param data
	 *            the new data added
	 * @return true:secceeded, false:failed
	 */

	public boolean addData(final SGData data, final String name,
			final SGIProgressControl progress, final float min, final float max) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		ArrayList list = new ArrayList(Arrays.asList(array));
		SGIFigureElementAxis aElement = this.getAxisElement();
		list.remove(aElement);
		list.add(0, aElement);

		SGIFigureElement[] newArray = (SGIFigureElement[]) list
				.toArray(new SGIFigureElement[] {});

		return this._addData(data, name, newArray, progress, min, max);
	}

	/**
	 * 
	 * @param data
	 * @param name
	 * @param p
	 * @return
	 */
	public boolean addData(final SGData data, final String name, final Map map) {
		this.addToList(data);

		List dList = this.getVisibleDataList();
		ArrayList nList = new ArrayList();
		for (int ii = 0; ii < dList.size(); ii++) {
			SGData d = (SGData) dList.get(ii);
			String n = this.getDataName(d);
			nList.add(n);
		}

		String nameNew = SGUtilityText.getSerialName(nList, name);

		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			Object value = map.get(new Integer(ii));
			SGProperties p = (SGProperties) value;
			if (array[ii].addData(data, nameNew, p) == false) {
				return false;
			}
		}

		this.broadcast(this.getIFigureElement(SGIFigureElementGraph.class), "");

		return true;
	}

	protected boolean _addData(final SGData data, final String name,
			final SGIFigureElement[] array, final SGIProgressControl progress,
			final float min, final float max) {
		final float ratio = (max - min) / array.length;
		this.addToList(data);
		for (int ii = 0; ii < array.length; ii++) {
			progress.setProgressValue(ratio * ii + min);
			array[ii].addData(data, name);
		}
		this.repaint();
		return true;
	}

	private void addToList(final SGData data) {
		this.mDataList.add(data);
	}

	/**
	 * ビューポートの境界を設定
	 */
	protected boolean setViewBounds() {
		final Rectangle2D rectView = this.mWnd.getViewportBounds();
		this.setViewBounds(rectView);
		return true;
	}

	/**
	 * 
	 */
	protected boolean setViewBounds(final Rectangle2D rectView) {
		// // set to the attribute
		// this.mViewBounds = rectView;

		// set to SGIFigureElement objects
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			array[ii].setViewBounds(rectView);
		}

		return true;
	}

	/**
	 * 引数は変化量
	 */
	public boolean resize(final float ratioX, final float ratioY) {

		this.mGraphRectX *= ratioX;
		this.mGraphRectY *= ratioY;
		this.mGraphRectWidth *= ratioX;
		this.mGraphRectHeight *= ratioY;

		//
		this.updateGraphRect();

		//
		this.setViewBounds();

		return true;

	}

	/**
	 * 
	 * @return
	 */
	public float getMagnification() {
		return this.mMagnification;
	}

	/**
	 * Zoom in/out this component.
	 * 
	 * @return true:secceeded, false:failed
	 */
	public boolean zoom(final float mag) {

		// 倍率を属性に設定
		this.mMagnification = mag;

		// SGIFigureElementに倍率を設定する
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			array[ii].zoom(mag);
		}
		this.updateGraphRect();

		// if the dragging rectanlgle is visible,
		// set it to be equal to the graph area rectangle
		// if( SGFigure.mRubberBandFlag )
		// {
		// this.setRubberBandRect();
		// }

		return true;
	}

	/**
	 * 
	 */
	private boolean mSelectionSymbolsVisibleFlag = true;

	/**
	 * 
	 * 
	 */
	protected void setSelectionSymbolsVisible(final boolean b) {
		this.mSelectionSymbolsVisibleFlag = b;
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			array[ii].setSymbolsVisibleAroundFocusedObjects(b);
		}
	}

	/**
	 * 
	 * 
	 */
	protected boolean isSelectionSymbolsVisible() {
		return this.mSelectionSymbolsVisibleFlag;
	}

	protected boolean setDirectlyBefore() {
		return this.prepare();
	}

	protected boolean setDirectlyAfter() {
		if (this.commit() == false) {
			return false;
		}
		this.notifyToRoot();
		this.repaint();
		return true;
	}

	public boolean setFigureXDirectly(final float value, final String unit) {
		if (this.setDirectlyBefore() == false)
			return false;
		if (this.setFigureX(value, unit) == false)
			return false;
		if (this.setDirectlyAfter() == false)
			return false;
		return true;
	}

	public boolean setFigureYDirectly(final float value, final String unit) {
		if (this.setDirectlyBefore() == false)
			return false;
		if (this.setFigureY(value, unit) == false)
			return false;
		if (this.setDirectlyAfter() == false)
			return false;
		return true;
	}

	public boolean setFigureLocationDirectly(final float value1,
			final String unit1, final float value2, final String unit2) {
		if (this.setDirectlyBefore() == false)
			return false;
		if (this.setFigureX(value1, unit1) == false)
			return false;
		if (this.setFigureY(value2, unit2) == false)
			return false;
		if (this.setDirectlyAfter() == false)
			return false;
		return true;
	}

	public boolean setFigureWidthDirectly(final float value, final String unit) {
		if (this.setDirectlyBefore() == false)
			return false;
		if (this.setFigureWidth(value, unit) == false)
			return false;
		if (this.setDirectlyAfter() == false)
			return false;
		return true;
	}

	public boolean setFigureHeightDirectly(final float value, final String unit) {
		if (this.setDirectlyBefore() == false)
			return false;
		if (this.setFigureHeight(value, unit) == false)
			return false;
		if (this.setDirectlyAfter() == false)
			return false;
		return true;
	}

	public boolean setFigureSizeDirectly(final float value1,
			final String unit1, final float value2, final String unit2) {
		if (this.setDirectlyBefore() == false)
			return false;
		if (this.setFigureWidth(value1, unit1) == false)
			return false;
		if (this.setFigureHeight(value2, unit2) == false)
			return false;
		if (this.setDirectlyAfter() == false)
			return false;
		return true;
	}

	public boolean setSpaceAxisLineAndNumberDirectly(final float value,
			final String unit) {
		if (this.setDirectlyBefore() == false)
			return false;
		if (this.setSpaceAxisLineAndNumber(value, unit) == false)
			return false;
		if (this.setDirectlyAfter() == false)
			return false;
		return true;
	}

	public boolean setSpaceNumberAndTitleDirectly(final float value,
			final String unit) {
		if (this.setDirectlyBefore() == false)
			return false;
		if (this.setSpaceNumberAndTitle(value, unit) == false)
			return false;
		if (this.setDirectlyAfter() == false)
			return false;
		return true;
	}

	public boolean setBackgroundColorDirectly(final Color cl) {
		if (this.setDirectlyBefore() == false)
			return false;
		if (this.setBackgroundColor(cl) == false)
			return false;
		if (this.setDirectlyAfter() == false)
			return false;
		return true;
	}

	public boolean setTransparentDirectly(final boolean b) {
		if (this.setDirectlyBefore() == false)
			return false;
		if (this.setTransparent(b) == false)
			return false;
		if (this.setDirectlyAfter() == false)
			return false;
		return true;
	}

	//
	// イベント関連
	//

	/**
	 * 
	 * @param e
	 * @return
	 */
	protected boolean onMouseMoved(final MouseEvent e) {
		final int x = e.getX();
		final int y = e.getY();

		// change the mouse cursor when the toggle button is not selected
		if (this.mWnd.isInsertFlagSelected() == false) {
			// set the mouse cursor
			Cursor cur = this.setMouseCursor(x, y);

			if (Cursor.getDefaultCursor().equals(cur) == false) {
				return true;
			}
		}
		// timing line
		else if (this.mWnd.getTimingLineInsertionFlag()) {
			SGIFigureElementTimingLine el = (SGIFigureElementTimingLine) this
					.getIFigureElement(SGIFigureElementTimingLine.class);
			if (el != null) {
				el.guideToAdd(x, y);
			}
		}

		return false;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @param str
	 * @return
	 */
	public boolean addString(final int x, final int y, final String str) {
		SGIFigureElementString el = (SGIFigureElementString) this
				.getIFigureElement(SGIFigureElementString.class);
		if (el != null) {
			return el.addString(x, y, str);
		}

		return false;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean addString(final int x, final int y) {
		SGIFigureElementString el = (SGIFigureElementString) this
				.getIFigureElement(SGIFigureElementString.class);
		if (el != null) {
			return el.addString(x, y);
		}

		return false;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean addAxisBreakSymbol(final int x, final int y) {
		SGIFigureElementAxisBreak el = (SGIFigureElementAxisBreak) this
				.getIFigureElement(SGIFigureElementAxisBreak.class);
		if (el != null) {
			return el.addAxisBreakSymbol(x, y);
		}

		return false;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean addTimingLine(final int x, final int y) {
		SGIFigureElementTimingLine el = (SGIFigureElementTimingLine) this
				.getIFigureElement(SGIFigureElementTimingLine.class);
		if (el != null) {
			return el.addTimingLine(x, y);
		}

		return false;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean addSignificantDifferenceSymbol(final int x, final int y) {
		SGIFigureElementSignificantDifference el = (SGIFigureElementSignificantDifference) this
				.getIFigureElement(SGIFigureElementSignificantDifference.class);
		if (el != null) {
			return el.addSignificantDifferenceSymbol(x, y);
		}

		return false;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean addShape(final int type, final int x, final int y) {
		SGIFigureElementShape el = (SGIFigureElementShape) this
				.getIFigureElement(SGIFigureElementShape.class);
		if (el != null) {
			return el.addShape(type, x, y);
		}

		return false;
	}

	protected boolean onFigureElementsKeyPressed(final KeyEvent e) {
		boolean effective = false;
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = array.length - 1; ii >= 0; ii--) {
			if (array[ii].onKeyPressed(e))
				effective = true;
		}
		return effective;
	}

	/**
	 * Called when the key pressed.
	 * 
	 * @param e
	 *            key event
	 * @return whether an effective when key pressed
	 */
	protected boolean onKeyPressed(final KeyEvent e) {
		boolean effective = false;

		effective = this.onFigureElementsKeyPressed(e);

		if (!this.isSelected())
			return effective;

		final int keycode = e.getKeyCode();
		int dx = 0;
		int dy = 0;
		switch (keycode) {
		case KeyEvent.VK_UP:
			dy = -1;
			break;
		case KeyEvent.VK_DOWN:
			dy = 1;
			break;
		case KeyEvent.VK_LEFT:
			dx = -1;
			break;
		case KeyEvent.VK_RIGHT:
			dx = 1;
			break;
		}
		if (dx != 0 || dy != 0) {
			Rectangle2D rect = this.getGraphRect();
			float interval = this.mMagnification;
			if (SGFigure.isSnappingToGrid())
				interval *= this.mWnd.getGridLineInterval();
			else
				interval *= (float) (SGIRootObjectConstants.GRID_INTERVAL_STEP_SIZE / SGIConstants.CM_POINT_RATIO);
			// horizontal
			if (dx != 0)
				rect.setRect(rect.getX() + interval * dx, rect.getY(), rect
						.getWidth(), rect.getHeight());
			// vertical
			if (dy != 0)
				rect.setRect(rect.getX(), rect.getY() + interval * dy, rect
						.getWidth(), rect.getHeight());
			// move figure
			this.setDraggingRect(rect);
			this.snapToLines(OTHER);
			this.setGraphRectOnDragging();

			effective = true;
		}

		return effective;
	}

	/**
	 * Called when the mouse is clicked.
	 * 
	 * @param e
	 *            mouse event
	 * @return whether an effective point is clicked
	 */
	protected boolean onMouseClicked(MouseEvent e) {

		// count
		final int count = e.getClickCount();

		// ask to SGIFigureElement objects
		SGIFigureElement el = this.onFigureElementClicked(e);
		if (el != null) {
			//
			this.mWnd.setFocusedFigure(this, false);

			// after treatment
			this.afterClicked(e);

			return true;
		}

		// if the rectangle of figure is clicked, the figure interpret
		// the it is clicked
		if (this.isGraphRectContains(e.getPoint())) {
			// update the list of focused figures
			this.updateFocusedFigureList(e);

			// aftertreatment
			this.afterClicked(e);

			// show pop-up menu
			if (SwingUtilities.isRightMouseButton(e) & count == 1) {
				// update pop-up menu
				this.updatePopupMenu();

				// show pop-up menu
				this.mPopupMenu.show(this.getComponent(), e.getX(), e.getY());
			}

			// show property dialog
			if (SwingUtilities.isLeftMouseButton(e) & count == 2) {
				this.mWnd.showPropertyDialogForSelectedFigures();
			}

			return true;

		}

		return false;

	}

	/**
	 * Flag whether this object is focused.
	 */
	private boolean mSelectedFlag = false;

	/**
	 * Get the flag as a focused object.
	 * 
	 * @return whether this object is focused.
	 */
	public boolean isSelected() {
		return this.mSelectedFlag;
	}

	/**
	 * Set the flag as a focused object.
	 * 
	 * @param b
	 *            focused
	 */
	public void setSelected(final boolean b) {
		this.mSelectedFlag = b;
	}

	/**
	 * 
	 * 
	 */
	public boolean hideSelectedObjects() {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			if (array[ii].hideSelectedObjects() == false) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 
	 * @param b
	 */
	protected void setSymbolsVisibleAroundAllObjects(boolean b) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			array[ii].setSymbolsVisibleAroundAllObjects(b);
		}
	}

	/**
	 * 
	 * @return
	 */
	protected boolean updatePopupMenu() {
		Component[] array = this.mPopupMenu.getComponents();
		for (int ii = 0; ii < array.length; ii++) {
			if (array[ii] instanceof JCheckBoxMenuItem) {
				JCheckBoxMenuItem item = (JCheckBoxMenuItem) array[ii];
				String com = item.getActionCommand();
				if (com.equals(MENUCMD_RUBBER_BANDING)) {
					item.setSelected(SGFigure.mRubberBandFlag);
				} else if (com.equals(MENUCMD_SHOW_BOUNDING_BOX)) {
					item.setSelected(SGFigure.mBoundingBoxVisibleFlag);
				} else if (com.equals(MENUBARCMD_SNAP_TO_GRID)) {
					item.setSelected(SGFigure.isSnappingToGrid());
				}
			}
		}

		return true;
	}

	/**
	 * 
	 */
	private boolean afterClicked(MouseEvent e) {
		this.repaint();
		return true;
	}

	/**
	 * 
	 */
	private SGIFigureElement onFigureElementClicked(MouseEvent e) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = array.length - 1; ii >= 0; ii--) {
			if (array[ii].onMouseClicked(e)) {
				return array[ii];
			}
		}

		return null;
	}

	/**
	 * Called when the mouse is pressed.
	 * 
	 * @param e
	 *            mouse event
	 * @return whether an effective point is pressed
	 */
	protected boolean onMousePressed(final MouseEvent e) {

		// if focused
		if (this.isSelected()) {
			// for anchors
			if (this.pressWithMouseLocation(e)) {
				return true;
			}
		}

		// press figure element
		if (this.pressFigureElement(e)) {
			return true;
		}

		// check whether the button is pressed in the rectangle
		if (this.isGraphRectContains(e.getPoint())) {
			return this.pressed(e);
		}

		return false;
	}

	// ask to SGIFigureElement objects
	private boolean pressFigureElement(MouseEvent e) {
		SGIFigureElement el = this.onFigureElementPressed(e);
		if (el != null) {
			// aftertreatment
			this.afterPressed(e);
			return true;
		}

		return false;
	}

	// check whether the button is pressed on an effective point
	private boolean pressWithMouseLocation(MouseEvent e) {
		if (this.mMouseLocation != OTHER) {
			return this.pressed(e);
		}
		return false;
	}

	//
	private boolean pressed(MouseEvent e) {
		// clear all selected objects in this figure
		this.clearFocusedObjects();

		// aftertreatment
		this.afterPressed(e);

		return true;
	}

	/**
	 * 
	 */
	private boolean afterPressed(MouseEvent e) {

		// set visible the rubber band
		if (SwingUtilities.isLeftMouseButton(e)) {
			SGFigure.mRubberBandVisibleFlag = true;

			// Change the cursor.
			if (this.mPressedElement == null) {
				if (this.mWnd.getCursor().equals(Cursor.getDefaultCursor())) {
					this.changeCursor();
					if (this.mMouseLocation == OTHER) {
						this.mWnd.setCursor(new Cursor(Cursor.MOVE_CURSOR));
					}
				}
			}
		}

		// マウスボタンを押下した点
		this.mPressedPoint.setLocation(e.getPoint());

		// マウス押下時のフィギュアの位置を記録
		this.recordFigureRect();

		return true;

	}

	/**
	 * Update the list of focused figures when the mouse is pressed.
	 * 
	 * @return
	 */
	protected boolean updateFocusedFigureList(final MouseEvent e) {
		final SGDrawingWindow wnd = this.getWindow();
		final ArrayList fList = wnd.getFocusedObjectsList();

		// Neither CTRL key nor SHIFT key is pressed.
		final int mod = e.getModifiers();
		if (((mod & InputEvent.CTRL_MASK) == 0)
				& ((mod & InputEvent.SHIFT_MASK) == 0)) {
			// If the list already contains this object.
			if (fList.contains(this)) {
				// There is nothing to do.
			} else {
				wnd.clearAllFocusedObjectsInFigures();
				wnd.setFocusedFigure(this, true);
			}

		}
		// otherwise
		else {
			// If the list already contains this object.
			if (fList.contains(this)) {
				final int loc = this.mMouseLocation;

				// except the four corners
				if (loc != NORTH_WEST & loc != NORTH_EAST & loc != SOUTH_EAST
						& loc != SOUTH_WEST) {
					// if no figure element is pressed, remove this figure
					// from the list of the selected figure
					if (this.mPressedElement == null) {
						wnd.setFocusedFigure(this, !this.isSelected());
					}
				}
			} else {
				wnd.setFocusedFigure(this, !this.isSelected());
			}
		}

		return true;
	}

	/**
	 * 
	 * @return
	 */
	boolean recordFigureRect() {
		this.mTempFigureRect.setRect(this.getGraphRectInClientRect());
		return true;
	}

	/**
	 * 
	 */
	private SGIFigureElement onFigureElementPressed(MouseEvent e) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = array.length - 1; ii >= 0; ii--) {
			// set temporary properties
			if (array[ii].setTemporaryPropertiesOfFocusedObjects() == false) {
				return null;
			}
			if (array[ii].onMousePressed(e)) {
				this.mPressedElement = array[ii];
				return array[ii];
			}
		}

		return null;
	}

	/**
	 * 
	 */
	protected boolean onMouseDragged(final MouseEvent e) {

		final int x = e.getX();
		final int y = e.getY();

		// notify to the pressed SGIFigureElement object
		if (this.mPressedElement != null) {
			ArrayList list = this.mPressedElement.getFocusedObjectsList();

			if (this.mPressedElement.onMouseDragged(e) == false) {
				return false;
			}

			if (list.size() != 0) {
				this.mWnd.moveFocusedObjects(x, y);
			}

			this.setCursorToWindow(this.mPressedElement);
			return true;
		}

		if (!this.isSelected()) {
			return false;
		}

		// mouse location
		final int ml = this.mMouseLocation;

		// other points
		if (ml == OTHER) {
			// parallel displacement
			this.mWnd.moveFocusedObjects(x, y);
			return true;
		}
		// record the temporary bounds
		this.recordFigureRect();

		// create a temporary object
		Point posNew = new Point(this.mPressedPoint);
		Rectangle2D rectNew = this.getDraggingRect();

		// update the rectangle
		SGUtility.resizeRectangle(rectNew, posNew, e, ml);

		// 幅が狭くなりすぎたらreturn
		if (rectNew.getWidth() < MIN_WIDTH | rectNew.getHeight() < MAX_WIDTH) {
			return true;
		}

		// set to an attribute
		this.mPressedPoint.setLocation(posNew);

		// update the graph rectangle
		this.setDraggingRect(rectNew);
		this.snapToLines(ml);

		// ドラッグ中の線を描画しない場合には、直ちにフィギュアに変更を反映する
		if (SGFigure.mRubberBandFlag == false) {
			// 矩形をもとにフィギュア境界を更新
			this.setGraphRectOnDragging();
		}

		return false;
	}

	/**
	 * 
	 * @param dx
	 * @param dy
	 * @return
	 */
	public void translate(final float dx, final float dy) {
		Rectangle2D dRect = this.getDraggingRect();
		this.setDraggingRect((float) dRect.getX() + dx, (float) dRect.getY()
				+ dy, (float) dRect.getWidth(), (float) dRect.getHeight());

		this.snapToLines(OTHER);

		// if the rectangle for dragging is invisible,
		// set the graph area rectangle immediately
		if (SGFigure.mRubberBandFlag == false) {
			this.mPressedPoint.setLocation(this.mPressedPoint.x + dx,
					this.mPressedPoint.y + dy);
			this.setGraphRectOnDragging();
		}

	}

	/**
	 * 
	 * @param dx
	 * @param dy
	 */
	public void translateSelectedObjects(final int dx, final int dy) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			if (array[ii].equals(this.mPressedElement)) {
				continue;
			}
			array[ii].translateFocusedObjects(dx, dy);
		}
	}

	//	
	Rectangle2D getRubberBandRect() {
		Rectangle2D rect = new Rectangle2D.Float();
		rect.setRect(this.mRubberBandRect);
		return rect;
	}

	//
	Rectangle2D getDraggingRect() {
		Rectangle2D rect = new Rectangle2D.Float();
		rect.setRect(this.mDraggingRect);
		return rect;
	}

	// set the rubber band
	void setRubberBandRect(final float x, final float y, final float w,
			final float h) {
		this.mRubberBandRect.setRect(x, y, w, h);
	}

	// set the dragging rectangle
	void setDraggingRect(final float x, final float y, final float w,
			final float h) {
		this.mDraggingRect.setRect(x, y, w, h);
	}

	// set the dragging rectangle
	void setDraggingRect(final Rectangle2D rect) {
		this.mDraggingRect.setRect(rect);
	}

	/**
	 * 
	 */
	protected boolean setGraphRectOnDragging() {
		Rectangle2D rbRect = this.getRubberBandRect();

		final float x = (float) rbRect.getX();
		final float y = (float) rbRect.getY();
		final float w = (float) rbRect.getWidth();
		final float h = (float) rbRect.getHeight();

		// returns if not changed
		Rectangle rectOld = this.getGraphRect().getBounds();
		if (rectOld.equals(rbRect.getBounds())) {
			return true;
		}

		// set the rectangle
		this.setGraphRectLocationAttributes(x, y);
		this.setGraphRectSizeAttributes(w, h);
		this.updateGraphRect();

		return true;
	}

	// // round off the number
	// private float roundOffInFigureLengthOrder( final float value )
	// {
	// final int order = SGIRootObjectConstants.MINIMAL_LENGTH_ORDER-1;
	// final float ratio = SGIConstants.CM_POINT_RATIO;
	// final float vCM = value*ratio;
	// final float r = (float)SGUtilityNumber.roundOffNumber( vCM, order );
	// final float v = r/ratio;
	// return v;
	// }

	// set the rubber band rectangle snapped to the lines
	void snapToLines(final int mouseLocation) {
		// System.out.println("snapToLines");
		if (isSnappingToGrid()) {
			this.snapToGrid(mouseLocation);
		} else {
			this.snapToUnitCell(mouseLocation);
		}
	}

	// set the rubber band rectangle snapped to the grid lines
	// with minimal width
	private void snapToUnitCell(final int mouseLocation) {
		final float eps = (float) SGUtilityNumber
				.getPowersOfTen(MINIMAL_LENGTH_ORDER);
		final float interval = this.mMagnification * eps / CM_POINT_RATIO;
		this.snap(interval, mouseLocation);
	}

	// set the rubber band rectangle snapped to the grid lines on the paper
	private void snapToGrid(final int mouseLocation) {
		final float interval = this.mMagnification
				* this.mWnd.getGridLineInterval();
		this.snap(interval, mouseLocation);
	}

	// set the rubber band rectangle snapped to the grid lines
	// with given interval
	private void snap(final float interval, final int mouseLocation) {
		final float px = this.mWnd.getPaperX();
		final float py = this.mWnd.getPaperY();

		Rectangle2D dRect = this.getDraggingRect();
		final float minX = (float) dRect.getMinX();
		final float maxX = (float) dRect.getMaxX();
		final float minY = (float) dRect.getMinY();
		final float maxY = (float) dRect.getMaxY();

		final float ox = minX;
		final float oy = maxY;

		final float ox2 = ox - px;
		final float oy2 = oy - py;

		final int nx = (int) (ox2 / interval);
		final int ny = (int) (oy2 / interval);

		// System.out.println(nx+" "+ny);

		final float rx = interval * nx;
		final float ry = interval * ny;

		int nxNew = nx;
		int nyNew = ny;
		if (ox2 - rx > interval / 2.0f) {
			nxNew++;
		}
		if (oy2 - ry > interval / 2.0f) {
			nyNew++;
		}

		// System.out.println(nxNew+" "+nyNew);

		// new origin
		final float oxNew = px + nxNew * interval;
		final float oyNew = py + nyNew * interval;

		// new bounds
		float xNew;
		float yNew;
		float wNew;
		float hNew;

		// x
		if (mouseLocation == WEST | mouseLocation == SOUTH_WEST
				| mouseLocation == NORTH_WEST) {
			xNew = oxNew;
			wNew = maxX - xNew;
		} else if (mouseLocation == EAST | mouseLocation == SOUTH_EAST
				| mouseLocation == NORTH_EAST) {
			xNew = minX;

			final int nMax = (int) ((maxX - px) / interval);
			final float rMax = interval * nMax;

			int nNew = nMax;
			if ((maxX - px) - rMax > interval / 2.0f) {
				nNew++;
			}

			final float maxNew = px + nNew * interval;
			wNew = maxNew - minX;
		} else if (mouseLocation == OTHER) {
			xNew = oxNew;
			wNew = (float) dRect.getWidth();
		} else {
			xNew = minX;
			wNew = (float) dRect.getWidth();
		}

		// y
		if (mouseLocation == SOUTH | mouseLocation == SOUTH_WEST
				| mouseLocation == SOUTH_EAST) {
			yNew = minY;
			hNew = oyNew - minY;
		} else if (mouseLocation == NORTH | mouseLocation == NORTH_EAST
				| mouseLocation == NORTH_WEST) {
			final int nMin = (int) ((minY - py) / interval);
			final float rMin = interval * nMin;

			int nNew = nMin;
			if ((minY - py) - rMin > interval / 2.0f) {
				nNew++;
			}

			final float minNew = py + nNew * interval;
			hNew = maxY - minNew;

			yNew = minNew;
		} else if (mouseLocation == OTHER) {
			hNew = (float) dRect.getHeight();
			yNew = oyNew - hNew;
		} else {
			yNew = minY;
			hNew = (float) dRect.getHeight();
		}

		// set new values to the rubber band rectangle
		this.setRubberBandRect(xNew, yNew, wNew, hNew);

		// System.out.println("new:"+this.getRubberBandRect());
		// System.out.println();

	}

	/**
	 * 
	 * @return
	 */
	protected boolean onMouseReleased(final MouseEvent e) {
		final int x = e.getX();
		final int y = e.getY();

		// set the mouse cursor
		if (this.mWnd.isInsertFlagSelected() == false) {
			this.setMouseCursor(x, y);
		}

		// set the rectangle of the graph area
		if (SGFigure.mRubberBandFlag & this.mPressedElement == null) {
			this.setGraphRectOnDragging();
		}

		// set invisible the rubber band
		if (SwingUtilities.isLeftMouseButton(e))
			SGFigure.mRubberBandVisibleFlag = false;

		// SGIFigureElementへの処理
		if (this.mPressedElement != null) {
			this.mPressedElement.onMouseReleased(e);
		}
		this.mPressedElement = null;

		return true;
	}

	/**
	 * 
	 */
	protected boolean isFigureMoved() {
		Rectangle temp = this.mTempFigureRect.getBounds();
		Rectangle present = this.getGraphRectInClientRect().getBounds();
		final boolean flag = !(temp.equals(present));
		return flag;
	}

	/**
	 * 
	 */
	public static final int DRAW_BACK_MARGIN = 2;

	/**
	 * 
	 * @return
	 */
	public boolean drawbackFigure() {
		final Rectangle2D cRect = this.mWnd.getClientRect();
		final Rectangle2D bbRect = this.getBoundingBox();
		final Rectangle2D pRect = this.mWnd.getPaperRect();

		final Rectangle2D rect = new Rectangle2D.Float();
		rect.setRect(bbRect);

		final int margin = DRAW_BACK_MARGIN;

		if (bbRect.getX() < cRect.getX()) {
			rect.setRect(margin, rect.getY() + margin, rect.getWidth(), rect
					.getHeight());
		}

		if (bbRect.getY() < cRect.getY()) {
			rect.setRect(rect.getX() + margin, margin, rect.getWidth(), rect
					.getHeight());
		}

		if (bbRect.getX() + bbRect.getWidth() > pRect.getX() + pRect.getWidth()) {
			rect.setRect(pRect.getX() + pRect.getWidth() - bbRect.getWidth()
					- margin, rect.getY() + margin, rect.getWidth(), rect
					.getHeight());
		}

		if (bbRect.getY() + bbRect.getHeight() > pRect.getY()
				+ pRect.getHeight()) {
			rect.setRect(rect.getX() + margin, pRect.getY() + pRect.getHeight()
					- bbRect.getHeight() - margin, rect.getWidth(), rect
					.getHeight());
		}

		if (this.setBoundingBox(rect) == false) {
			return false;
		}

		// snap to the lines
		this.snapToLines(OTHER);
		this.setGraphRectOnDragging();

		return true;
	}

	/**
	 * Clear all focused objects in SGIFigureElement objects.
	 * 
	 * @return
	 */
	public boolean clearFocusedObjects() {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			if (array[ii].clearFocusedObjects() == false) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 
	 * @return
	 */
	public boolean setMementoBackward() {
		if (this.mUndoManager.setMementoBackward() == false) {
			return false;
		}

		this.updateGraphRect();

		return true;
	}

	/**
	 * 
	 * @return
	 */
	public boolean setMementoForward() {
		if (this.mUndoManager.setMementoForward() == false) {
			return false;
		}

		this.updateGraphRect();

		return true;
	}

	/**
	 * Update the graph rect.
	 * 
	 */
	void updateGraphRect() {
		final float x = this.getGraphRectX();
		final float y = this.getGraphRectY();
		final float w = this.getGraphRectWidth();
		final float h = this.getGraphRectHeight();

		// set to the rubber band
		this.setRubberBandRect(x, y, w, h);
		this.setDraggingRect(x, y, w, h);

		// set to SGIFigureElement objects
		SGIFigureElement[] array = this.getIFigureElementArray();
		if (array != null) {
			for (int ii = 0; ii < array.length; ii++) {
				array[ii].setGraphRect(x, y, w, h);
			}
		}

		this.repaint();

	}

	/**
	 * 
	 * 
	 */
	public void notifyToRoot() {
		this.mWnd.notifyToRoot();
	}

	/**
	 * 
	 * @return
	 */
	public boolean isChanged() {
		return this.mUndoManager.isChanged();
	}

	/**
	 * 
	 * @return
	 */
	public boolean isChangedRoot() {
		if (this.isChanged()) {
			return true;
		}

		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			if (array[ii].isChangedRoot()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 
	 */
	public void setChanged(final boolean b) {
		this.mUndoManager.setChanged(b);
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	private Cursor setMouseCursor(final int x, final int y) {
		this.setMouseLocation(x, y);
		if (this.mMouseLocation == OTHER) {
			if (this.onDrawingElement(x, y) == false) {
				return this.setMouseCursor();
			}
		} else {
			return this.setMouseCursor();
		}

		return null;
	}

	private Cursor setMouseCursor() {
		if (this.isSelected() == false) {
			Cursor cur = Cursor.getDefaultCursor();
			this.setMouseCursor(cur);
			return cur;
		}
		return this.changeCursor();
	}

	/**
	 * マウス位置を属性に設定
	 */
	private void setMouseLocation(final int x, final int y) {
		final float radius = 1.50f * SGClientPanel.getAnchorSize();
		// final Rectangle2D rect = this.getGraphRect();
		final Rectangle2D rect = this.getRubberBandRect();
		this.mMouseLocation = SGUtility.getMouseLocation(rect, x, y, radius);
	}

	/**
	 * 
	 */
	private Cursor changeCursor() {

		// マウスの位置に応じてカーソルを変更
		Cursor cur = null;
		switch (this.mMouseLocation) {
		case WEST: {
			cur = new Cursor(Cursor.W_RESIZE_CURSOR);
			break;
		}
		case EAST: {
			cur = new Cursor(Cursor.E_RESIZE_CURSOR);
			break;
		}
		case NORTH: {
			cur = new Cursor(Cursor.N_RESIZE_CURSOR);
			break;
		}
		case SOUTH: {
			cur = new Cursor(Cursor.S_RESIZE_CURSOR);
			break;
		}
		case NORTH_WEST: {
			cur = new Cursor(Cursor.NW_RESIZE_CURSOR);
			break;
		}
		case SOUTH_EAST: {
			cur = new Cursor(Cursor.SE_RESIZE_CURSOR);
			break;
		}
		case NORTH_EAST: {
			cur = new Cursor(Cursor.NE_RESIZE_CURSOR);
			break;
		}
		case SOUTH_WEST: {
			cur = new Cursor(Cursor.SW_RESIZE_CURSOR);
			break;
		}
		default: {
			cur = Cursor.getDefaultCursor();
		}
		}

		// set the cursor to the window
		// if set to the figure, the cursor does not change
		this.setMouseCursor(cur);

		return cur;
	}

	private void setMouseCursor(Cursor cur) {
		this.mWnd.setCursor(cur);
	}

	/**
	 * 
	 */
	private boolean onDrawingElement(final int x, final int y) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = array.length - 1; ii >= 0; ii--) {
			if (array[ii].onDrawingElement(x, y)) {
				this.setCursorToWindow(array[ii]);
				return true;
			}
		}

		this.setMouseCursor(Cursor.getDefaultCursor());

		return false;
	}

	/**
	 * 
	 * @return
	 */
	protected boolean setCursorToWindow(final SGIFigureElement el) {
		Cursor cur = el.getFigureElementCursor();
		if (cur != null) {
			this.mWnd.setCursor(cur);
		}
		return true;
	}

	/**
	 * 
	 */
	public static final String MENUCMD_RUBBER_BANDING = "Rubber Banding";

	/**
	 * 
	 */
	public static final String MENUCMD_SAVE_PROPERTY = "Save Property";

	/**
	 * 
	 */
	public static final String MENUCMD_PROPERTY = "Property";

	/**
	 * 
	 */
	public static final String MENUCMD_GRID_VISIBLE = "Grid Visible";

	/**
	 * 
	 */
	public static final String MENUCMD_GRID_PROPERTY = "Grid Property";

	/**
	 * 
	 */
	private boolean createPopupMenu() {
		JPopupMenu p = this.mPopupMenu;
		p.setBounds(0, 0, 100, 100);

		p.add(new JLabel("  -- Figure --"));
		p.addSeparator();

		JCheckBoxMenuItem item;

		// draw rectangle
		item = SGUtility.addCheckBoxItem(p, this, MENUCMD_RUBBER_BANDING);
		item.setSelected(SGFigure.mRubberBandFlag);

		// snap to grid
		item = SGUtility.addCheckBoxItem(p, this, MENUBARCMD_SNAP_TO_GRID);
		item.setSelected(SGFigure.mSnapToGridFlag);

		// show bounding box
		item = SGUtility.addCheckBoxItem(p, this, MENUCMD_SHOW_BOUNDING_BOX);
		item.setSelected(SGFigure.mBoundingBoxVisibleFlag);

		p.addSeparator();

		// save property
		SGUtility.addItem(p, this, MENUCMD_SAVE_PROPERTY);

		p.addSeparator();

		// move to back or front
		SGUtility.addItem(p, this, MENUCMD_MOVE_TO_FRONT);
		SGUtility.addItem(p, this, MENUCMD_MOVE_TO_BACK);
		p.addSeparator();

		// cut
		SGUtility.addItem(p, this, MENUCMD_CUT);

		// copy
		SGUtility.addItem(p, this, MENUCMD_COPY);

		// paste
		SGUtility.addItem(p, this, MENUCMD_PASTE);

		p.addSeparator();

		// delete
		SGUtility.addItem(p, this, MENUCMD_DELETE);

		// duplicate
		SGUtility.addItem(p, this, MENUCMD_DUPLICATE);

		p.addSeparator();

		// property
		SGUtility.addItem(p, this, MENUCMD_PROPERTY);

		return true;
	}

	/**
	 * 
	 */
	public boolean prepare() {
		this.mTemporaryProperties = this.getProperties();
		return true;
	}

	/**
	 * 
	 * @return
	 */
	protected abstract boolean createDialog();

	/**
	 * 
	 */
	public SGProperties getProperties() {
		SGFigure.FigureProperties p = new SGFigure.FigureProperties();
		this.getProperties(p);
		return p;
	}

	/**
	 * 
	 * @param p
	 * @return
	 */
	public boolean getProperties(SGProperties p) {
		if ((p instanceof FigureProperties) == false)
			return false;

		FigureProperties fp = (FigureProperties) p;

		fp.setX(this.mGraphRectX);
		fp.setY(this.mGraphRectY);
		fp.setWidth(this.mGraphRectWidth);
		fp.setHeight(this.mGraphRectHeight);
		fp.setSpaceLineAndNumber(this.getAxisElement()
				.getSpaceAxisLineAndNumber());
		fp.setSpaceNumberAndTitle(this.getAxisElement()
				.getSpaceNumberAndTitle());
		fp.setBackgroundColor(this.mBackgroundColor);
		fp.setTransparent(this.mTransparentFlag);
		fp.setLegendVisible(this.getLegendElement().isLegendVisible());

		return true;
	}

	/**
	 * Returns a property dialog.
	 * 
	 * @return property dialog
	 */
	public SGPropertyDialog getPropertyDialog() {
		return this.mDialog;
	}

	/**
	 * アクションが発生すると呼び出されます。
	 */
	public void actionPerformed(final ActionEvent e) {
		// System.out.println("<< SGFigure::actionPerformed >>");
		// System.out.println("id="+e.getID());
		// System.out.println();

		final String command = e.getActionCommand();
		final Object source = e.getSource();

		{
			//
			// popup menu
			//

			if (command.equals(MENUCMD_SHOW_BOUNDING_BOX)) {
				mBoundingBoxVisibleFlag = !mBoundingBoxVisibleFlag;
				this.updatePopupMenu();
				this.repaint();
				return;
			} else if (command.equals(MENUCMD_RUBBER_BANDING)) {
				SGFigure.mRubberBandFlag = !SGFigure.mRubberBandFlag;
				this.updatePopupMenu();
				this.repaint();
				return;
			} else if (command.equals(MENUBARCMD_SNAP_TO_GRID)) {
				SGFigure.setSnappingToGrid(!SGFigure.isSnappingToGrid());
				this.updatePopupMenu();
				this.mWnd.updateSnapToGridItems();
				return;
			} else if (command.equals(MENUCMD_SAVE_PROPERTY)) {
				this.mWnd.createPropertyFileFromFocusedFigures();
			} else if (command.equals(MENUCMD_PROPERTY)) {
				this.mWnd.showPropertyDialogForSelectedFigures();
			} else if (command.equals(MENUCMD_MOVE_TO_FRONT)) {
				this.mWnd.moveFocusedObjectsToFront();
			} else if (command.equals(MENUCMD_MOVE_TO_BACK)) {
				this.mWnd.moveFocusedObjectsToBack();
			} else if (command.equals(MENUCMD_CUT)) {
				this.mWnd.doCut();
			} else if (command.equals(MENUCMD_COPY)) {
				this.mWnd.doCopy();
			} else if (command.equals(MENUCMD_PASTE)) {
				this.mWnd.doPaste();
			} else if (command.equals(MENUCMD_DELETE)) {
				this.mWnd.doDelete();
			} else if (command.equals(MENUCMD_DUPLICATE)) {
				this.mWnd.doDuplicate();
			}

		}

		//
		// SGIFIgureElementからのイベントへの対応
		//

		if (source instanceof SGIFigureElement) {

			// イベントを投げたSGIFigureElementオブジェクトの取得
			final SGIFigureElement element = (SGIFigureElement) e.getSource();

			// SGIFigureElementの変更を伝える場合
			if (command.equals(SGIFigureElement.NOTIFY_CHANGE)) {
				// broadcastして同期を取る
				this.broadcast(element, command);
			}
			// change on undo
			else if (command.equals(SGIFigureElement.NOTIFY_CHANGE_ON_UNDO)) {
				// broadcastして同期を取る
				this.broadcast(element, command);
			}
			// 共通プロパテイ設定
			else if (command
					.equals(SGIFigureElement.SET_PROPERTIES_OF_SELECTED_OBJECTS)) {
				//
				this.mWnd.showPropertyDialogForSelectedObjects(this, element);
			} else if (command.equals(SGIFigureElement.CLEAR_FOCUSED_OBJECTS)) {
				this.mWnd.clearAllFocusedObjectsInFigures();
			}
			//
			else if (command.equals(SGIFigureElement.NOTIFY_CHANGE_TO_ROOT)) {
				this.notifyToRoot();
			} else if (command.equals(SGIFigureElement.NOTIFY_CHANGE_CURSOR)) {
				this.setCursorToWindow(element);
			} else if (command.equals(SGIFigureElement.MERGE_DATA)) {
				// データの同期を取る
				this.mergeData(element);
			}

		}

		return;

	}

	/**
	 * データの同期を取る
	 */
	private boolean mergeData(final SGIFigureElement element) {
		// SGIFigureElementオブジェクトが持っているデータリストと、
		// SGFigureが持っているデータリストの差分を取る
		final List elementDataList = element.getDataList();
		final List differenceDataList = new ArrayList();

		List dList = this.mDataList;
		for (int ii = 0; ii < dList.size(); ii++) {
			final SGData data = (SGData) dList.get(ii);
			if (!elementDataList.contains(data)) {
				differenceDataList.add(data);
			}
		}

		for (int ii = 0; ii < differenceDataList.size(); ii++) {
			final SGData data = (SGData) differenceDataList.get(ii);
			this.removeData(data);
		}

		return true;

	}

	//
	private SGUndoManager mUndoManager = new SGUndoManager(this);

	/**
	 * 
	 */
	public boolean initPropertiesHistory() {
		return this.mUndoManager.initPropertiesHistory();
	}

	/**
	 * アンドゥ操作の実行
	 */
	public boolean undo() {
		return this.mUndoManager.undo();
	}

	/**
	 * 
	 */
	public boolean redo() {
		return this.mUndoManager.redo();
	}

	/**
	 * 
	 * @return
	 */
	public boolean commit() {
		// ダイアログを出す前後でプロパティが変更されている場合のみ、
		// 履歴を更新する
		SGProperties pTemp = this.mTemporaryProperties;
		SGProperties pPresent = this.getProperties();
		if (pTemp.equals(pPresent) == false) {
			this.mUndoManager.setChanged(true);
		}

		this.mTemporaryProperties = null;

		this.updateGraphRect();
		// this.repaint();

		return true;
	}

	/**
	 * 
	 */
	public boolean preview() {
		this.updateGraphRect();

		return true;
	}

	/**
	 * 
	 */
	public boolean cancel() {
		this.setProperties(this.mTemporaryProperties);

		this.updateGraphRect();

		this.mTemporaryProperties = null;

		return true;
	}

	/**
	 * 
	 * @return
	 */
	public SGProperties getMemento() {
		return this.getProperties();
	}

	/**
	 * 
	 * @param p
	 * @return
	 */
	public boolean setMemento(SGProperties p) {
		return this.setProperties(p);
	}

	/**
	 * 
	 */
	public boolean updateHistory() {

		// ArrayList changedObjList = new ArrayList();
		// if( this.mUndoManager.isChanged() )
		// {
		// this.mUndoManager.updateThisObjectHistory();
		// changedObjList.add(this);
		// this.setChanged(false);
		// }
		//
		// SGIFigureElement[] array = this.getIFigureElementArray();
		// for( int ii=0; ii<array.length; ii++ )
		// {
		// SGIFigureElement element = array[ii];
		// if( element.isChanged() )
		// {
		// element.updateHistory();
		// changedObjList.add( element );
		//
		// // special case : because the visibility of legend element can be
		// controlled from SGFigure,
		// // SGFigure is added to the history list when properties of the
		// legend element is changed.
		// if( element.equals(this.mLegendElement) )
		// {
		// if( changedObjList.contains(this)==false )
		// {
		// this.mUndoManager.updateThisObjectHistory();
		// changedObjList.add(this);
		// this.setChanged(false);
		// }
		// }
		// }
		// }
		//
		// if( changedObjList.size()!=0 )
		// {
		// //System.out.println(changedObjList);
		// this.mUndoManager.updateObjectHistory( changedObjList );
		// }

		ArrayList list = new ArrayList();
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			SGIFigureElement element = array[ii];
			list.add(element);

			// particular case
			if (element.equals(this.getLegendElement())) {
				// element.setChanged(true);
			}
		}

		this.mUndoManager.updateHistory(list);

		return true;
	}

	/**
	 * 
	 */
	public void initUndoBuffer() {
		// this object
		this.mUndoManager.initUndoBuffer();

		// figure elements
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			array[ii].initUndoBuffer();
		}
	}

	/**
	 * 
	 * @return
	 */
	public boolean isUndoable() {
		return this.mUndoManager.isUndoable();
	}

	/**
	 * 
	 * @return
	 */
	public boolean isRedoable() {
		return this.mUndoManager.isRedoable();
	}

	/**
	 * 
	 * @return
	 */
	public boolean initGraphAreaLocation() {
		// System.out.println("<< SGFigure : initGraphAreaLocation >>");

		SGTuple2f topAndBottom = new SGTuple2f();
		SGTuple2f leftAndRight = new SGTuple2f();
		if (this.calcMargin(topAndBottom, leftAndRight) == false) {
			return false;
		}

		// 計算結果を基に決定
		float topMax = topAndBottom.x;
		float leftMax = leftAndRight.x;

		// SGFigureに設定
		this.mGraphRectX = leftMax;
		this.mGraphRectY = topMax;

		// 属性の全てのSGIFigureElementに値を設定し、
		// 描画要素を作り直させる
		final SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			array[ii].setGraphRectLocation(this.mGraphRectX, this.mGraphRectY);
			array[ii].setGraphRectSize(this.mGraphRectWidth,
					this.mGraphRectHeight);
		}

		return true;
	}

	/**
	 * 
	 * @param topAndBottom
	 * @param leftAndRight
	 * @return
	 */
	public boolean calcMargin(final SGTuple2f topAndBottom,
			final SGTuple2f leftAndRight) {

		// 属性のSGIFIgureElementに対して、グラフ描画領域の原点と
		// コンポーネントのサイズを計算させる
		final SGIFigureElement[] array = this.getIFigureElementArray();

		final SGTuple2f[] tbArray = new SGTuple2f[array.length];
		final SGTuple2f[] lrArray = new SGTuple2f[array.length];

		for (int ii = 0; ii < array.length; ii++) {
			tbArray[ii] = new SGTuple2f();
			lrArray[ii] = new SGTuple2f();
			final boolean flag = array[ii].getMarginAroundGraphRect(
					tbArray[ii], lrArray[ii]);
			if (!flag) {
				return false;
			}
			// System.out.println(ii+" "+tbArray[ii]+" "+lrArray[ii]);
		}
		// System.out.println();

		// 計算結果を基に決定
		float topMax = 0.0f;
		float bottomMax = 0.0f;
		float leftMax = 0.0f;
		float rightMax = 0.0f;
		for (int ii = 0; ii < array.length; ii++) {
			// System.out.println("ii="+ii);

			final float top = tbArray[ii].x;
			final float bottom = tbArray[ii].y;
			final float left = lrArray[ii].x;
			final float right = lrArray[ii].y;

			// System.out.println(top+" "+bottom+" "+left+" "+right);

			if (top > topMax) {
				topMax = top;
			}
			if (bottom > bottomMax) {
				bottomMax = bottom;
			}
			if (left > leftMax) {
				leftMax = left;
			}
			if (right > rightMax) {
				rightMax = right;
			}
		}
		// System.out.println();

		final float mag = this.mMagnification;
		topAndBottom.x = topMax + mag * MARGIN_TOP;
		topAndBottom.y = bottomMax + mag * MARGIN_BOTTOM;
		leftAndRight.x = leftMax + mag * MARGIN_LEFT;
		leftAndRight.y = rightMax + mag * MARGIN_RIGHT;

		return true;
	}

	/**
	 * 
	 * @return
	 */
	public Rectangle2D getBoundingBox() {
		final Rectangle2D graphAreaRect = this.getGraphRect();

		SGTuple2f tb = new SGTuple2f();
		SGTuple2f lr = new SGTuple2f();
		if (this.calcMargin(tb, lr) == false) {
			return null;
		}

		Rectangle2D rect = new Rectangle2D.Float((float) graphAreaRect.getX()
				- lr.x, (float) graphAreaRect.getY() - tb.x,
				(float) graphAreaRect.getWidth() + lr.x + lr.y,
				(float) graphAreaRect.getHeight() + tb.x + tb.y);

		return rect;
	}

	public static final int MARGIN_TOP = 5;
	public static final int MARGIN_BOTTOM = 5;
	public static final int MARGIN_LEFT = 5;
	public static final int MARGIN_RIGHT = 5;

	/**
	 * 
	 * @return
	 */
	public boolean setBoundingBox(final Rectangle2D bbRect) {
		if (bbRect == null) {
			return false;
		}

		SGTuple2f tb = new SGTuple2f();
		SGTuple2f lr = new SGTuple2f();
		if (this.calcMargin(tb, lr) == false) {
			return false;
		}

		final float x = (float) bbRect.getX() + lr.x;
		final float y = (float) bbRect.getY() + tb.x;
		final float w = (float) bbRect.getWidth() - (lr.x + lr.y);
		final float h = (float) bbRect.getHeight() - (tb.x + tb.y);

		this.setGraphRect(x, y, w, h);

		return true;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean setCenter(final float cx, final float cy) {
		Rectangle2D bbRect = this.getBoundingBox();
		final float w = (float) bbRect.getWidth();
		final float h = (float) bbRect.getHeight();

		final float x = cx - w / 2.0f;
		final float y = cy - h / 2.0f;

		Rectangle2D rect = new Rectangle2D.Float(x, y, w, h);
		if (this.setBoundingBox(rect) == false) {
			return false;
		}

		return true;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean setGraphRectLocationByLeftBottom(final float x, final float y) {
		Rectangle2D gRect = this.getGraphRect();
		this.setGraphRectLocation(x, y - (float) gRect.getHeight());
		return true;
	}

	/**
	 * 
	 */
	private boolean broadcast(final SGIFigureElement element, String msg) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			array[ii].synchronize(element, msg);
		}
		return true;
	}

	/**
	 * 
	 */
	public boolean removeData(final SGData data) {
		SGIFigureElement[] array = this.getIFigureElementArray();

		List dList = this.mDataList;
		for (int ii = dList.size() - 1; ii >= 0; ii--) {
			final SGData data_ = (SGData) dList.get(ii);
			if (data_.equals(data)) {
				data.dispose();
				dList.remove(ii);
				for (int jj = 0; jj < array.length; jj++) {
					array[jj].removeData(data);
				}

				return true;
			}
		}

		return false;
	}

	/**
	 * Returns the list of data.
	 * 
	 * @return a list of data
	 */
	public List getDataList() {
		return new ArrayList(this.mDataList);
	}

	/**
	 * Returns the list of visible data.
	 * 
	 * @return a list of visible data
	 */
	public List getVisibleDataList() {
		return this.getGraphElement().getVisibleDataList();
	}

	/**
	 * 
	 */
	public void paint(final Graphics g, final boolean clip) {
		this.paintGraphics((Graphics2D) g, clip);
	}

	/**
	 * 
	 * @param g
	 * @param clip
	 */
	private void paintGraphics(Graphics2D g2d, boolean clip) {

		// fill the background
		if (this.mTransparentFlag == false) {
			final Rectangle2D graphAreaRect = this.getGraphRect();
			g2d.setPaint(this.mBackgroundColor);
			g2d.fill(graphAreaRect);
		}

		// draw the bounding box
		if (SGFigure.mBoundingBoxVisibleFlag
				& this.mSelectionSymbolsVisibleFlag) {
			Rectangle2D rect = this.getBoundingBox();
			if (rect != null) {
				g2d.setPaint(Color.BLUE);
				g2d.setStroke(new BasicStroke(1));
				g2d.draw(rect);
			}
		}

		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			array[ii].paint(g2d, clip);
		}

	}

	/**
	 * 
	 * @param document
	 * @param el
	 * @return
	 */
	public Element createElement(final Document document) {
		Element el = document.createElement(SGFigure.TAG_NAME_FIGURE);

		// property of figure
		if (this.writeProperty(el) == false) {
			return null;
		}

		if (this.createElementLower(document, el) == false) {
			return null;
		}

		return el;
	}

	/**
	 * 
	 * @param el
	 * @return
	 */
	public Element createElementForFocusedInBoundingBox(final Document document) {
		Element el = document.createElement(SGFigure.TAG_NAME_FIGURE);

		// property of figure
		if (this.writePropertyOnFocusedInBoundingBox(el) == false) {
			return null;
		}

		if (this.createElementLower(document, el) == false) {
			return null;
		}

		return el;
	}

	/**
	 * 
	 * @param el
	 * @return
	 */
	public Element createElementForFocusedForDuplication(final Document document) {
		Element el = document.createElement(SGFigure.TAG_NAME_FIGURE);

		// property of figure
		if (this.writePropertyForDuplication(el) == false) {
			return null;
		}

		if (this.createElementLower(document, el) == false) {
			return null;
		}

		return el;
	}

	private boolean createElementLower(final Document document,
			final Element parent) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			Element el = array[ii].createElement(document);
			if (array[ii] instanceof SGIFigureElementGraph) {
				continue;
			}
			if (el == null) {
				return false;
			}
			parent.appendChild(el);
		}

		ArrayList elList = new ArrayList();
		if (this.getGraphElement().createElementOfData(document, elList) == false) {
			return false;
		}
		for (int ii = 0; ii < elList.size(); ii++) {
			Element el = (Element) elList.get(ii);
			parent.appendChild(el);
		}

		return true;
	}

	public static final String TAG_NAME_FIGURE = "Figure";
	public static final String KEY_FIGURE_TYPE = "Type";
	public static final String KEY_FIGURE_X_IN_CLIENT = "X";
	public static final String KEY_FIGURE_Y_IN_CLIENT = "Y";
	public static final String KEY_FIGURE_WIDTH = "Width";
	public static final String KEY_FIGURE_HEIGHT = "Height";
	public static final String KEY_SPACE_AXIS_LINE_AND_NUMBER = "SpaceAxisLineAndNumber";
	public static final String KEY_SPACE_NUMBER_AND_TITLE = "SpaceNumberAndTitle";
	public static final String KEY_FIGURE_BACKGROUND_COLOR = "BackgroundColor";
	public static final String KEY_FIGURE_BACKGROUND_TRANSPARENT = "BackgroundTransparent";

	public boolean writeProperty(final Element el) {
		final float x = this.mGraphRectX;
		final float y = this.mGraphRectY;
		return this.writeProperty_(el, x, y);
	}

	public boolean writePropertyOnFocusedInBoundingBox(final Element el) {
		final SGDrawingWindow wnd = this.getWindow();
		final Rectangle2D bb = wnd.getBoundingBoxOfFigures(wnd
				.getFocusedObjectsList());
		final float x = this.mGraphRectX - (float) (bb.getX())
				/ this.mMagnification;
		final float y = this.mGraphRectY - (float) (bb.getY())
				/ this.mMagnification;
		return this.writeProperty_(el, x, y);
	}

	public boolean writePropertyForDuplication(final Element el) {
		final float offset = this.DUPLICATION_OFFSET * this.mMagnification;
		final float x = this.mGraphRectX + offset;
		final float y = this.mGraphRectY + offset;
		return this.writeProperty_(el, x, y);
	}

	private final float DUPLICATION_OFFSET = 20.0f;

	/**
	 * 
	 */
	public abstract String getClassType();

	/**
	 * 
	 * @return
	 */
	private boolean writeProperty_(final Element el, final float x,
			final float y) {
		final float ratio = SGIConstants.CM_POINT_RATIO;

		el.setAttribute(KEY_FIGURE_TYPE, this.getClassType());

		final float x_ = (float) SGUtilityNumber.roundOffNumber(x * ratio, -2);
		final float y_ = (float) SGUtilityNumber.roundOffNumber(y * ratio, -2);
		el.setAttribute(KEY_FIGURE_X_IN_CLIENT, Float.toString(x_)
				+ SGIConstants.cm);
		el.setAttribute(KEY_FIGURE_Y_IN_CLIENT, Float.toString(y_)
				+ SGIConstants.cm);

		final float w = (float) SGUtilityNumber.roundOffNumber(
				this.mGraphRectWidth * ratio, -2);
		final float h = (float) SGUtilityNumber.roundOffNumber(
				this.mGraphRectHeight * ratio, -2);
		el.setAttribute(KEY_FIGURE_WIDTH, Float.toString(w) + SGIConstants.cm);
		el.setAttribute(KEY_FIGURE_HEIGHT, Float.toString(h) + SGIConstants.cm);

		el.setAttribute(KEY_SPACE_AXIS_LINE_AND_NUMBER, Float.toString(this
				.getSpaceAxisLineAndNumber()
				* ratio)
				+ SGIConstants.cm);
		el.setAttribute(KEY_SPACE_NUMBER_AND_TITLE, Float.toString(this
				.getSpaceNumberAndTitle()
				* ratio)
				+ SGIConstants.cm);
		el.setAttribute(KEY_FIGURE_BACKGROUND_COLOR, SGUtilityText
				.getColorString(this.mBackgroundColor));
		el.setAttribute(KEY_FIGURE_BACKGROUND_TRANSPARENT, Boolean
				.toString(this.mTransparentFlag));
		// el.setAttribute( PF_LEGEND_VISIBLE, Boolean.toString(
		// this.mLegendElement.isLegendVisible() ) );

		return true;
	}

	/**
	 * 
	 * @param el
	 * @return
	 */
	public boolean readProperty(final Element el) {
		String str = null;
		Number num = null;
		Boolean b = null;
		Color cl = null;

		// x
		str = el.getAttribute(SGFigure.KEY_FIGURE_X_IN_CLIENT);
		if (str.length() != 0) {
			StringBuffer ux = new StringBuffer();
			num = SGUtilityText.getNumber(str, ux);
			if (num == null) {
				return false;
			}
			final float x = num.floatValue();
			if (this.setFigureX(x, ux.toString()) == false) {
				return false;
			}
		}

		// y
		str = el.getAttribute(SGFigure.KEY_FIGURE_Y_IN_CLIENT);
		if (str.length() != 0) {
			StringBuffer uy = new StringBuffer();
			num = SGUtilityText.getNumber(str, uy);
			if (num == null) {
				return false;
			}
			final float y = num.floatValue();
			if (this.setFigureY(y, uy.toString()) == false) {
				return false;
			}
		}

		// width
		str = el.getAttribute(SGFigure.KEY_FIGURE_WIDTH);
		if (str.length() != 0) {
			StringBuffer uWidth = new StringBuffer();
			num = SGUtilityText.getNumber(str, uWidth);
			if (num == null) {
				return false;
			}
			final float width = num.floatValue();
			if (this.setFigureWidth(width, uWidth.toString()) == false) {
				return false;
			}
		}

		// height
		str = el.getAttribute(SGFigure.KEY_FIGURE_HEIGHT);
		if (str.length() != 0) {
			StringBuffer uHeight = new StringBuffer();
			num = SGUtilityText.getNumber(str, uHeight);
			if (num == null) {
				return false;
			}
			final float height = num.floatValue();
			if (this.setFigureHeight(height, uHeight.toString()) == false) {
				return false;
			}
		}

		// background color
		str = el.getAttribute(SGFigure.KEY_FIGURE_BACKGROUND_COLOR);
		if (str.length() != 0) {
			cl = SGUtilityText.getColorFromString(str);
			if (cl == null) {
				return false;
			}
			if (this.setBackgroundColor(cl) == false) {
				return false;
			}
		}

		// transparent
		str = el.getAttribute(SGFigure.KEY_FIGURE_BACKGROUND_TRANSPARENT);
		if (str.length() != 0) {
			b = SGUtilityText.getBoolean(str);
			if (b == null) {
				return false;
			}
			final boolean transparent = b.booleanValue();
			if (this.setTransparent(transparent) == false) {
				return false;
			}
		}

		return true;
	}

	/**
	 * 
	 */
	public boolean createDataObjectFromPropertyFile(final Element elData,
			final SGData data) {
		this.mDataList.add(data);

		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			if (array[ii].createDataObject(elData, data) == false) {
				return false;
			}
		}

		return true;
	}

	/**
	 * 
	 */
	public boolean setProperties(SGProperties p) {
		if ((p instanceof FigureProperties) == false)
			return false;

		FigureProperties fp = (FigureProperties) p;

		this.mGraphRectX = fp.mX;
		this.mGraphRectY = fp.mY;
		this.mGraphRectWidth = fp.mWidth;
		this.mGraphRectHeight = fp.mHeight;

		this.setBackgroundColor(fp.mBackgroundColor);
		this.setTransparent(fp.mTransparentFlag);

		// set to the axis
		this.setSpaceAxisLineAndNumber(fp.mSpaceLineAndNumber);
		this.setSpaceNumberAndTitle(fp.mSpaceNumberAndTitle);

		// set to the legend
		this.setLegendVisible(fp.mLegendVisibleFlag);

		return true;
	}

	/**
	 * Duplicate the focused objects.
	 * 
	 * @return
	 */
	protected boolean duplicateFocusedObjects() {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			if (array[ii].duplicateFocusedObjects() == false) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Create a list of copied objects from focused objects.
	 * 
	 * @return list of copies of focused objects
	 */
	protected ArrayList createCopiedObjects() {
		ArrayList list = new ArrayList();
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			list.addAll(array[ii].getCopiedObjectsList());
		}

		return list;
	}

	/**
	 * Create a list of cut objects from focused objects.
	 * 
	 * @return list of cut objects
	 */
	protected ArrayList cutFocusedObjects() {
		ArrayList list = new ArrayList();
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			list.addAll(array[ii].cutFocusedObjects());
		}

		return list;
	}

	/**
	 * Create copies of focused data objects.
	 * 
	 * @param dataList -
	 *            list of focused data objects
	 * @param dataNameList -
	 *            list of data names
	 * @return true:succeeded, false:failed
	 */
	boolean createCopiedDataObjects(ArrayList dataList, ArrayList dataNameList,
			ArrayList propertiesMapList) {
		ArrayList fList = this.getGraphElement().getFocusedDataList();
		for (int ii = 0; ii < fList.size(); ii++) {
			SGData data = (SGData) fList.get(ii);
			dataList.add(data);

			String name = this.getDataName(data);
			dataNameList.add(name);

			Map map = this.getDataPropertiesMap(data);
			propertiesMapList.add(map);
		}

		return true;
	}

	/**
	 * Create a list of cut data objects from focused objects.
	 * 
	 * @param dataList -
	 *            list of focused data objects
	 * @param dataNameList -
	 *            list of data names
	 * @return true:succeeded, false:failed
	 */
	boolean cutFocusedDataObjects(ArrayList dataList, ArrayList dataNameList,
			ArrayList propertiesMapList) {
		ArrayList fList = this.getGraphElement().cutFocusedData();
		for (int ii = 0; ii < fList.size(); ii++) {
			SGData data = (SGData) fList.get(ii);
			dataList.add(data);

			String name = this.getDataName(data);
			dataNameList.add(name);

			Map map = this.getDataPropertiesMap(data);
			propertiesMapList.add(map);
		}

		return true;
	}

	/**
	 * Returns the name of data.
	 * 
	 * @param data -
	 *            data object
	 * @return the name of data
	 */
	public String getDataName(SGData data) {
		return this.getGraphElement().getDataName(data);
	}

	/**
	 * 
	 * @param name
	 * @param data
	 * @return
	 */
	public boolean setDataName(String name, SGData data) {
		return this.getGraphElement().setDataName(name, data);
	}

	/**
	 * 
	 * @param data
	 * @return
	 */
	public Map getDataPropertiesMap(SGData data) {
		TreeMap map = new TreeMap();

		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			SGProperties p = array[ii].getDataProperties(data);
			map.put(new Integer(ii), p);
		}

		return map;
	}

	/**
	 * Paste the objects.
	 * 
	 * @param list
	 *            of the objects to be pasted
	 * @return true:succeeded, false:failed
	 */
	public boolean paste(ArrayList list) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			if (array[ii].paste(list) == false) {
				return false;
			}
		}

		return true;
	}

	/**
	 * 
	 * @param toFront
	 * @return
	 */
	public boolean moveFocusedObjects(final boolean toFront) {
		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			if (array[ii].moveFocusedObjects(toFront) == false) {
				return false;
			}
		}

		return true;
	}

	public void dispose() {
		this.mComponent = null;
		this.mWnd = null;
		this.mDataList.clear();

		Map map = this.mFigureElementMap;
		Iterator itr = map.values().iterator();
		while (itr.hasNext()) {
			Object obj = itr.next();
			SGIFigureElement el = (SGIFigureElement) obj;
			el.dispose();
		}

		map.clear();
	}

	private int mMode = MODE_DISPLAY;

	public void setMode(final int mode) {
		this.mMode = mode;

		SGIFigureElement[] array = this.getIFigureElementArray();
		for (int ii = 0; ii < array.length; ii++) {
			array[ii].setMode(mode);
		}
	}

	public int getMode() {
		return this.mMode;
	}

	/**
	 * 
	 */
	public static class FigureProperties extends SGProperties {
		private float mX;
		private float mY;
		private float mWidth;
		private float mHeight;
		private float mSpaceLineAndNumber;
		private float mSpaceNumberAndTitle;
		private Color mBackgroundColor;
		private boolean mTransparentFlag;
		private boolean mLegendVisibleFlag;

		public FigureProperties() {
			super();
		}

		public void dispose() {
			this.mBackgroundColor = null;
		}

		/**
		 * 
		 */
		public boolean equals(final Object obj) {
			if ((obj instanceof FigureProperties) == false) {
				return false;
			}

			FigureProperties p = (FigureProperties) obj;

			if (p.mX != this.mX)
				return false;
			if (p.mY != this.mY)
				return false;
			if (p.mWidth != this.mWidth)
				return false;
			if (p.mHeight != this.mHeight)
				return false;
			if (p.mSpaceLineAndNumber != this.mSpaceLineAndNumber)
				return false;
			if (p.mSpaceNumberAndTitle != this.mSpaceNumberAndTitle)
				return false;
			if (p.mBackgroundColor.equals(this.mBackgroundColor) == false)
				return false;
			if (p.mTransparentFlag != this.mTransparentFlag)
				return false;
			if (p.mLegendVisibleFlag != this.mLegendVisibleFlag)
				return false;

			return true;

		}

		public void setX(final float value) {
			this.mX = value;
		}

		public void setY(final float value) {
			this.mY = value;
		}

		public void setWidth(final float value) {
			this.mWidth = value;
		}

		public void setHeight(final float value) {
			this.mHeight = value;
		}

		public void setSpaceLineAndNumber(final float value) {
			this.mSpaceLineAndNumber = value;
		}

		public void setSpaceNumberAndTitle(final float value) {
			this.mSpaceNumberAndTitle = value;
		}

		public void setBackgroundColor(final Color cl) {
			this.mBackgroundColor = cl;
		}

		public void setTransparent(final boolean b) {
			this.mTransparentFlag = b;
		}

		public void setLegendVisible(final boolean b) {
			this.mLegendVisibleFlag = b;
		}

		public float getX() {
			return this.mX;
		}

		public float getY() {
			return this.mY;
		}

		public float getWidth() {
			return this.mWidth;
		}

		public float getHeight() {
			return this.mHeight;
		}

		public float getSpaceLineAndNumber() {
			return this.mSpaceLineAndNumber;
		}

		public float getSpaceNumberAndTitle() {
			return this.mSpaceNumberAndTitle;
		}

		public Color getBackgroundColor() {
			return this.mBackgroundColor;
		}

		public boolean isTransparent() {
			return this.mTransparentFlag;
		}

		public boolean isLegendVisible() {
			return this.mLegendVisibleFlag;
		}

	}

	/**
	 * @return
	 */
	public static boolean isSnappingToGrid() {
		return mSnapToGridFlag;
	}

	/**
	 * @param b
	 */
	public static void setSnappingToGrid(final boolean b) {
		mSnapToGridFlag = b;
	}

}
