
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.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;

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

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


/**
 * A figure.
 */

public abstract class SGFigure extends JLayeredPane
	implements ActionListener,
		SGIConstants, SGIUndoable, SGIMovable, SGISelectable, SGIPaintable,
		SGIFigureDialogObserver, SGINode, SGIRootObjectConstants
{


	/**
	 * ID-number of this figure.
	 */
	private int mID;


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


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


	/**
	 * A SGIGraphElement object on this figure.
	 */
	protected SGIGraphElement mGraphElement = null;


	/**
	 * A SGILegendElement object on this figure.
	 */
	protected SGILegendElement mLegendElement = null;


	/**
	 * A SGIStringElement object on this figure.
	 */
	protected SGIStringElement mStringElement = null;


	/**
	 * A SGIAxisElement object on this figure.
	 */
	protected SGIAxisElement mAxisElement = null;


	/**
	 * A SGIAxisBreakElement object on this figure.
	 */
	protected SGIAxisBreakElement mAxisBreakElement = null;
	
	
	/**
	 * A SGISignificantDifferenceElement object on this figure.
	 */
	protected SGISignificantDifferenceElement mSignificantDifferenceElement = null;
	
	
	/**
	 * A SGITimingLineElement object on this figure.
	 */
	protected SGITimingLineElement mTimingLineElement = null;


	/**
	 * A SGGridElement object on this figure.
	 */
	protected SGIGridElement mGridElement = null;

	
	/**
	 * X-coordinate of the graph rectangle in the client area.
	 * 倍率100%のときの、クライアント領域におけるグラフ描画領域のX座標
	 */
	protected float mGraphRectX = 0.0f;


	/**
	 * Y-coordinate of the graph rectangle in the client area.
	 * 倍率100%のときの、クライアント領域におけるグラフ描画領域のY座標
	 */
	protected float mGraphRectY = 0.0f;


	/**
	 * 倍率100%のときの、グラフ描画領域の幅
	 */
	protected float mGraphRectWidth = 0.0f;


	/**
	 * 倍率100%のときの、グラフ描画領域の高さ
	 */
	protected float mGraphRectHeight = 0.0f;



	/**
	 * 倍率
	 */
	protected float mMagnification = 1.0f;


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


	/**
	 * A dialog.
	 */
	protected SGPropertyDialog mDialog = null;


	/**
	 * 
	 */
	protected SGIFigureElement mPressedElement = null;


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


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


	/**
	 * 
	 */
	protected Rectangle2D mViewBounds = null;


	/**
	 * 
	 */
	protected Color mBackgroundColor;


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


	/**
	 * Whether to snap to grid.
	 */
	protected static boolean mSnapToGridFlag = SGDefaultValues.FIGURE_SNAP_TO_GRID;


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


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


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


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


	/**
	 * 
	 */
	protected float mMinWidth = 50.0f;



	/**
	 * 
	 */
	protected float mMinHeight = 50.0f;


	/**
	 * 
	 */
	private static boolean mBoundingBoxVisibleFlag = SGDefaultValues.FIGURE_BOUNDING_BOX_VISIBLE;


	/**
	 * 
	 */
	protected boolean mUpperFitFlag = false;


	/**
	 * 
	 */
	protected boolean mLowerFitFlag = false;


	/**
	 * 
	 */
	protected boolean mLeftFitFlag = false;


	/**
	 * 
	 */
	protected boolean mRightFitFlag = false;


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


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



	/**
	 * Constants of layer.
	 */
	public static final int LAYER_GRID = 10;
	public static final int LAYER_TIMING_LINE = 20;
	public static final int LAYER_GRAPH = 30;
	public static final int LAYER_SIGNIFICANT_DIFFERENCE = 40;
	public static final int LAYER_AXIS = 50;
	public static final int LAYER_AXIS_BREAK = 60;
	public static final int LAYER_LEGEND = 70;
	public static final int LAYER_STRING = 80;


	/**
	 * 
	 */
	protected static final int OTHER = 0;


	/**
	 * 
	 */
	protected static final int NORTH = 1;


	/**
	 * 
	 */
	protected static final int SOUTH = 2;


	/**
	 * 
	 */
	protected static final int WEST = 3;


	/**
	 * 
	 */
	protected static final int EAST = 4;


	/**
	 * 
	 */
	protected static final int NORTH_WEST = 5;


	/**
	 * 
	 */
	protected static final int NORTH_EAST = 6;


	/**
	 * 
	 */
	protected static final int SOUTH_WEST = 7;


	/**
	 * 
	 */
	protected static final int SOUTH_EAST = 8;



	/**
	 * 
	 */
	public static final String NOTIFY_CHANGE = "Notify the change";



	/**
	 * 
	 */
	public static final String SET_PROPERTIES_OF_SELECTED_OBJECTS = "Set properties of selected objects";


	/**
	 * 
	 */
	public static final String CLEAR_FOCUSED_OBJECTS = "Clear focused objects";


	/**
	 * 
	 */
	public static final String NOTIFY_CHANGE_TO_ROOT = "Notify the change to root";

	
	/**
	 * 
	 */
	public static final String NOTIFY_CHANGE_CURSOR = "Notify the change of cursor";

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


	/**
	 * 
	 */
	public SGFigure()
	{
		super();
		this.create();
	}



	/**
	 * 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.create();
	}



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



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

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

		return true;
	}



	/**
	 * 
	 */
	protected boolean isFocusedFigure()
	{
		final ArrayList fList = this.getWindow().getFocusedObjectsList();
		return fList.contains(this);
	}






	//
	// setters and getters
	//


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


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



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


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


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


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



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

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


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


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



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



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



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


	/**
	 * 
	 * @param element
	 * @return
	 */
	public void setGraphElement( final SGIGraphElement element )
	{
		mGraphElement = element;
	}


	/**
	 * 
	 * @param element
	 * @return
	 */
	public void setLegendElement( final SGILegendElement element )
	{
		mLegendElement = element;
	}


	/**
	 * 
	 * @param element
	 * @return
	 */
	public void setStringElement( final SGIStringElement element )
	{
		mStringElement = element;
	}


	/**
	 * 
	 * @param element
	 * @return
	 */
	public void setAxisElement( final SGIAxisElement element )
	{
		mAxisElement = element;
	}

	
	/**
	 * 
	 * @param element
	 * @return
	 */
	public void setAxisBreakElement( final SGIAxisBreakElement element )
	{
		this.mAxisBreakElement = element;
	}

	
	/**
	 * 
	 * @param element
	 */
	public void setSignificantDifferenceElement( final SGISignificantDifferenceElement element )
	{
		this.mSignificantDifferenceElement = element;
	}

	
	/**
	 * 
	 * @param element
	 */
	public void setTimingLineElement( final SGITimingLineElement element )
	{
		this.mTimingLineElement = element;
	}


	/**
	 * 
	 * @param element
	 */
	public void setGridElement( final SGIGridElement element )
	{
		this.mGridElement = element;
	}

	
	/**
	 * 
	 */
	public SGIGraphElement getGraphElement()
	{
		return this.mGraphElement;
	}

	/**
	 * 
	 */
	public SGIAxisElement getAxisElement()
	{
		return this.mAxisElement;
	}

	/**
	 * 
	 */
	public SGILegendElement getLegendElement()
	{
		return this.mLegendElement;
	}

	/**
	 * 
	 */
	public SGIStringElement getStringElement()
	{
		return this.mStringElement;
	}


	/**
	 * 
	 * @return
	 */
	public SGIAxisBreakElement getAxisBreakElement()
	{
		return this.mAxisBreakElement;
	}
	
	
	/**
	 * 
	 * @return
	 */
	public SGISignificantDifferenceElement getSignigicantDifferenceElement()
	{
		return this.mSignificantDifferenceElement;
	}

	
	/**
	 * 
	 * @return
	 */
	public SGITimingLineElement getTimingLineElement()
	{
		return this.mTimingLineElement;
	}
	
	
	/**
	 * 
	 * @return
	 */
	public SGIGridElement getGridElement()
	{
		return this.mGridElement;
	}
	
	
	/**
	 * An array of SGIFigureElement objects.
	 */
	private SGIFigureElement[] mFigureElementArray;


	/**
	 * Set the array of SGIFigureElement objects.
	 * @param array
	 */
	public void setFigureElementArray( SGIFigureElement[] array )
	{
		this.mFigureElementArray = (SGIFigureElement[])array.clone();
	}


	/**
	 * Get the array of SGIFigureElement objects.
	 * @return the array of SGIFigureElement objects
	 */
	public SGIFigureElement[] getIFigureElementArray()
	{
		return this.mFigureElementArray;
	}



	/**
	 * Returns a list of child nodes.
	 * @return a list of chid nodes
	 */
	public ArrayList getChildNodes()
	{
		ArrayList list = new ArrayList( Arrays.asList( this.getIFigureElementArray() ) );
		return list;
	}


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


	/**
	 * 
	 * @return
	 */
	public String getInstanceDescription()
	{
		final float ratio = SGIConstants.CM_POINT_RATIO;
		final int order = SGFigure.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;
	}



	/**
	 * Returns an array of SGIFigureElement starting from the foreground.
	 * @return an array of SGIFigureElement
	 */
	public SGIFigureElement[] getIFigureElementArrayFromLayer()
	{
		ArrayList list = new ArrayList();
		Component[] cArray = this.getComponents();
		for( int ii=0; ii<cArray.length; ii++ )
		{
			if( cArray[ii] instanceof SGIFigureElement )
			{
				list.add( cArray[ii] );
			}
		}
		SGIFigureElement[] array = new SGIFigureElement[list.size()];
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii] = (SGIFigureElement)cArray[ii];
		}
		return array;
	}



	/**
	 * 
	 * @param el
	 * @return
	 */
	SGIFigureElement getIFigureElement( SGIFigureElement el )
	{
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii].getClass().equals( el.getClass() ) )
			{
				return array[ii];
			}
		}
		return null;
	}


	/**
	 * 
	 * @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);
	}


	/**
	 * 
	 */
	protected Rectangle2D getViewBounds()
	{
		return this.mViewBounds;
	}



	/**
	 * 
	 */
	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 getFigureY()
	{
		return this.mGraphRectY;
	}


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


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


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


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


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


	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( SGFigure.MINIMAL_LENGTH_ORDER );

		return (diff<ten);
	}


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



	/**
	 * 引数はピクセル単位の座標値
	 */
	private boolean setGraphRectLocation2(
		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;
	}



	private Rectangle2D getRectangleRoundingOut( final Rectangle2D rect )
	{
		final float x = (float)rect.getX();
		final float y = (float)rect.getY();
		final float w = (float)rect.getWidth();
		final float h = (float)rect.getHeight();
		return this.getRectangleRoundingOut( x, y, w, h );
	}


	private Rectangle2D getRectangleRoundingOut( final float xPt, final float yPt, final float widthPt, final float heightPt )
	{

		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;


		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;


		Rectangle2D rectNew = new Rectangle2D.Float( x, y, width, height );
		
		return rectNew;
	}



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


	/**
	 * 
	 */
	private boolean setGraphRectSize2(
		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.setGraphRectLocation2(x,y);
		this.setGraphRectSize2(w,h);
		this.updateGraphRect();
		return true;
	}



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


	/**
	 * Add a new data.
	 * @param data the new data added
	 * @return true:secceeded, false:failed
	 */
	public boolean addData( final SGData data )
	{
		return this.addData( data, "data" );
	}



	/**
	 * 
	 * @param data
	 * @param name
	 * @return
	 */
	public boolean addData( final SGData data, final String name )
	{
		this.mDataList.add( data );

		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii].addData( data, name );
		}

		return true;
	}


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

		ArrayList 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.getGraphElement() );

		return true;
	}



	/**
	 * ビューポートの境界を設定
	 */
	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 setSize( final SGTuple2f size )
	{
		this.setSize(
			(int)size.x, (int)size.y
		);
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int jj=0; jj<array.length; jj++ )
		{
			array[jj].setISize( new SGTuple2f( size ) );
		}

		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;

	}



	/**
	 * 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;
	}


	/**
	 * 
	 */
	private boolean setFitFlag()
	{
/*
		//
		Rectangle2D cRect = mWnd.getClientRect();


		// grid lines
		double[] vGridRatioArray = this.mWnd.getVerticalGridRatioArray();
		double[] hGridRatioArray = this.mWnd.getHorizontalGridRatioArray();

		int[] vGridArray = new int[vGridRatioArray.length];
		int[] hGridArray = new int[hGridRatioArray.length];

//System.out.println("<v>");
		for( int ii=0; ii<vGridArray.length; ii++ )
		{
			vGridArray[ii] = (int)(vGridRatioArray[ii]*cRect.getWidth());
//System.out.println(vGridArray[ii]);
		}
//System.out.println();

//System.out.println("<h>");
		for( int ii=0; ii<hGridArray.length; ii++ )
		{
			hGridArray[ii] = (int)(hGridRatioArray[ii]*cRect.getHeight());
//System.out.println(hGridArray[ii]);
		}
//System.out.println();


		// フィギュアの境界線がグリッド線に一致している場合の処理
		int x = this.getX();
		int y = this.getY();
		int w = this.getWidth();
		int h = this.getHeight();

//System.out.println(x+"  "+y+"  "+(x+w)+"  "+(y+h));
//System.out.println();

		this.mUpperFitFlag = false;
		this.mLowerFitFlag = false;
		this.mLeftFitFlag = false;
		this.mRightFitFlag = false;

		for( int ii=0; ii<vGridArray.length; ii++ )
		{
			if( x == vGridArray[ii] )
			{
				this.mLeftFitFlag = true;
//System.out.println("x:"+x);
				break;
			}
		}

		for( int ii=0; ii<vGridArray.length; ii++ )
		{
			if( x + w == vGridArray[ii] )
			{
				this.mRightFitFlag = true;
//System.out.println("x+w:"+(x+w));
				break;
			}
		}

		for( int ii=0; ii<hGridArray.length; ii++ )
		{
			if( y == hGridArray[ii] )
			{
				this.mUpperFitFlag = true;
//System.out.println("y:"+y);
				break;
			}
		}

		for( int ii=0; ii<hGridArray.length; ii++ )
		{
			if( y + h == hGridArray[ii] )
			{
				this.mLowerFitFlag = true;
//System.out.println("y+h:"+(y+h));
				break;
			}
		}

*/
		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.isInsertToggleButtonSelected() == 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.isInsertTimingLineButtonSelected() )
		{
			this.mTimingLineElement.guideToAdd(x,y);
		}

		return false;
	}



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

		boolean flag = false;
		
		// a label
		if( this.mWnd.isInsertLabelButtonSelected() )
		{
			flag = this.mStringElement.addString(x,y);
		}

		// a timing line
		if( this.mWnd.isInsertTimingLineButtonSelected() )
		{
			flag = this.mTimingLineElement.addTimingLine(x,y);
		}

		// an axis break symbol
		if( this.mWnd.isInsertBreakButtonSelected() )
		{
			flag = this.mAxisBreakElement.addAxisBreakSymbol(x,y);
		}

		// a symbol of siginificant difference
		if( this.mWnd.isInsertSignificantDifferenceSymbolButtonSelected() )
		{
			flag = this.mSignificantDifferenceElement.addSignificantDifferenceSymbol(x,y);
		}

		return flag;
	}




	/**
	 * 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();
		final int mod = e.getModifiers();

		
		// ask to SGIFigureElement objects
		SGIFigureElement el = this.onFigureElementClicked(e);
		if( el!=null )
		{
			//
			this.mWnd.setFocusedFigure( this, false );
			
			// aftertreatment
			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);

			this.mWnd.setFocusedFigure( this, true );

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

				// show pop-up menu
				this.mPopupMenu.show( this, 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( MENUCMD_GRID_VISIBLE ) )
//				{
//					item.setSelected( this.mGridElement.isGridVisible() );
//				}
			}
		}

		return true;
	}



	/**
	 * 
	 */
	private boolean afterClicked( MouseEvent e )
	{
		// 自分自身を最前面に出す処理を、所属するウインドウに依頼する
//		this.mWnd.moveToFront(this);

		return true;
	}



	/**
	 * 
	 */
	private SGIFigureElement onFigureElementClicked( MouseEvent e )
	{
		SGIFigureElement[] array = this.getIFigureElementArrayFromLayer();
		for( int ii=0; ii<array.length; 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.isFocusedFigure() )
		{
			// 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
		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.mWnd.moveToFront(this);


		// マウスボタンを押下した点
		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 & MouseEvent.CTRL_MASK ) == 0 )
			& ( ( mod & MouseEvent.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, false );
					}
				}
			}
			else
			{
				wnd.setFocusedFigure( this, true );
			}
		}

		return true;
	}




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


	/**
	 * 
	 */
	private SGIFigureElement onFigureElementPressed( MouseEvent e )
	{
		SGIFigureElement[] array = this.getIFigureElementArrayFromLayer();
		for( int ii=0; ii<array.length; 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)
	{

		if( this.mWnd.isInsertToggleButtonSelected() )
		{
			return false;
		}


		// if the right button is pressed, there is nothing to do
		// for the mouse drag event
		if( SwingUtilities.isRightMouseButton(e) )
		{
			return true;
		}


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

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

			if( list.size()!=0 )
			{
				this.mWnd.moveFocusedObjects( e.getX(),e.getY() );
			}
			
			this.setCursorToWindow( mPressedElement );
			return true;
		}


		if( !this.isFocusedFigure() )
		{
			return true;
		}


		// other points
		if( mMouseLocation == OTHER )
		{
			final boolean flag = this.dragOtherPoint(e);
			return flag;
		}


		// グラフ描画領域の変更前の状態
		Rectangle2D dRect = this.getDraggingRect();
		final float xOld = (float)dRect.getX();
		final float yOld = (float)dRect.getY();
		final float wOld = (float)dRect.getWidth();
		final float hOld = (float)dRect.getHeight();

		
		// create a temporary object
		Point pos = new Point( this.mPressedPoint );


		// 位置を記録
		this.recordFigureRect();


		final int diffX = e.getX() - pos.x;
		final int diffY = e.getY() - pos.y;

//System.out.println("event: "+e.getPoint());
//System.out.println("pressed point: "+this.mPressedPoint);
//System.out.println("Old: "+this.mRectangleOnDragging);
//System.out.println(this.getGraphAreaRect());
//System.out.println("diff: "+diffX+"  "+diffY);


		final float sizeOldX = wOld;
		final float sizeOldY = hOld;

		float sizeNewX = 0.0f;
		float sizeNewY = 0.0f;


		// 以下の処理で更新する量
		// そのままグラフ描画領域の矩形になる
		float x = xOld;
		float y = yOld;
		float w = wOld;
		float h = hOld;


		//
		// ドラッグしている点に応じて場合分け
		//
		// 

		final int ml = this.mMouseLocation;

		if( ml == NORTH )
		{
//System.out.println("NORTH");
			pos.setLocation(
				pos.getX(),
				pos.getY() + diffY
			);
			sizeNewY = sizeOldY - diffY;
			y = yOld + sizeOldY - sizeNewY;
			h = sizeNewY;
		}
		else if( ml == SOUTH )
		{
//System.out.println("SOUTH");
			pos.setLocation(
				pos.getX(),
				pos.getY() + diffY
			);
			sizeNewY = sizeOldY + diffY;
			h = sizeNewY;
		}
		else if( ml == WEST )
		{
//System.out.println("WEST");
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY() + diffY
			);
			sizeNewX = sizeOldX - diffX;
			x = xOld + sizeOldX - sizeNewX;
			w = sizeNewX;
		}
		else if( ml == EAST )
		{
//System.out.println("EAST");
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY()
			);
			sizeNewX = sizeOldX + diffX;
			w = sizeNewX;
		}
		else if( ml == NORTH_WEST )
		{
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY() + diffY
			);
			sizeNewX = sizeOldX - diffX;

			// Shiftキーが押されていたら縦横比は変えない
			if( (e.getModifiers()&MouseEvent.SHIFT_MASK) != 0 )
			{
				sizeNewY = sizeNewX*( sizeOldY/sizeOldX );
			}
			else
			{
				sizeNewY = sizeOldY - diffY;
			}
			x = xOld + sizeOldX - sizeNewX;
			y = yOld + sizeOldY - sizeNewY;
			w = sizeNewX;
			h = sizeNewY;
//System.out.println(sizeOldY+"  "+sizeNewY);
		}
		else if(
			( ml == NORTH_EAST )
			&& ( (e.getModifiers()&MouseEvent.SHIFT_MASK) == 0 )
		)
		{
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY() + diffY
			);

			// Shiftキーが押されていないので縦横比は可変
			sizeNewX = sizeOldX + diffX;
			sizeNewY = sizeOldY - diffY;

			y = yOld + sizeOldY - sizeNewY;
			w = sizeNewX;
			h = sizeNewY;
		}
		else if(
			( ml == NORTH_EAST )
			&& ( (e.getModifiers()&MouseEvent.SHIFT_MASK) != 0 )
		)
		{
			pos.setLocation(
				pos.getX(),
				pos.getY() + diffY
			);

			// Shiftキーが押されているので縦横比は不変
			sizeNewY = sizeOldY - diffY;
			sizeNewX = sizeNewY*( sizeOldX/sizeOldY );

			y = yOld + sizeOldY - sizeNewY;
			w = sizeNewX;
			h = sizeNewY;
		}
		else if(
			( ml == SOUTH_WEST )
			&& ( (e.getModifiers()&MouseEvent.SHIFT_MASK) == 0 )
		)
		{

			pos.setLocation(
				pos.getX() + diffX,
				pos.getY() + diffY
			);

			// Shiftキーが押されていないので縦横比は可変
			sizeNewX = sizeOldX - diffX;
			sizeNewY = sizeOldY + diffY;

			x = xOld + sizeOldX - sizeNewX;
			w = sizeNewX;
			h = sizeNewY;
		}
		else if(
			( ml == SOUTH_WEST )
			&& ( (e.getModifiers()&MouseEvent.SHIFT_MASK) != 0 )
		)
		{
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY() + diffY
			);

			// Shiftキーが押されているので縦横比は不変
			sizeNewX = sizeOldX - diffX;
			sizeNewY = sizeNewX*( sizeOldY/sizeOldX );

			x = xOld + sizeOldX - sizeNewX;
			w = sizeNewX;
			h = sizeNewY;
		}
		else if(
			( ml == SOUTH_EAST )
			&& ( (e.getModifiers()&MouseEvent.SHIFT_MASK) == 0 )
		)
		{
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY() + diffY
			);

			// Shiftキーが押されていないので縦横比は可変
			sizeNewX = sizeOldX + diffX;
			sizeNewY = sizeOldY + diffY;

			w = sizeNewX;
			h = sizeNewY;
		}
		else if(
			( ml == SOUTH_EAST )
			&& ( (e.getModifiers()&MouseEvent.SHIFT_MASK) != 0 )
		)
		{
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY()
			);

			// Shiftキーが押されているので縦横比は不変
			sizeNewX = sizeOldX + diffX;
			sizeNewY = sizeNewX*( sizeOldY/sizeOldX );

			w = sizeNewX;
			h = sizeNewY;
		}


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

		
		// 幅が狭くなりすぎたらreturn
		if( w<mMinWidth || h<mMinHeight )
		{
			return true;
		}


		// 矩形を更新
		this.setDraggingRect( x, y, w, h );
		this.snapToLines( ml );


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

		return true;
	}



	/**
	 * 
	 */
	private boolean dragOtherPoint( final MouseEvent e )
	{
		if( this.isSelected() == false )
		{
			return false;
		}
		
		// parallel displacement
		if( this.isGraphRectContains( this.mPressedPoint ) )
		{
			this.mWnd.moveFocusedObjects( e.getX(), e.getY() );
			return true;
		}

		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].translateSelectedObjects(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);
	}


	/**
	 * 
	 */
	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();

//		// round off the numbers
//		final float rx = this.roundOffInFigureLengthOrder(x);
//		final float ry = this.roundOffInFigureLengthOrder(y);
//		final float rw = this.roundOffInFigureLengthOrder(w);
//		final float rh = this.roundOffInFigureLengthOrder(h);

//System.out.println();

		// set the rectangle
		this.setGraphRectLocation(x,y);
		this.setGraphRectSize(w,h);
		this.updateGraphRect();

		return true;
	}


	// round off the number
	private float roundOffInFigureLengthOrder( final float value )
	{
		final int order = SGFigure.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;
//System.out.println(vCM+"  "+r);
		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.isInsertToggleButtonSelected() == false )
		{
			this.setMouseCursor(x,y);
		}

		// set the rectangle of the graph area
		if( SGFigure.mRubberBandFlag )
		{
			this.setGraphRectOnDragging();
		}
		
		// set invisible the rubber band
		SGFigure.mRubberBandVisibleFlag = false;

		// SGIFigureElementへの処理
		if( mPressedElement != null )
		{
			mPressedElement.onMouseReleased(e);
		}
		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
	 */
	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 undo()
	{
		this.mFigureStateCounter--;

		FigureProperties p = (FigureProperties)this.mFigurePropertyHistoryList.get(this.mFigureStateCounter);
		this.setProperties(p);

		this.updateGraphRect();

		return true;
	}



	/**
	 * 
	 * @return
	 */
	public boolean redo()
	{
		this.mFigureStateCounter++;

		FigureProperties p = (FigureProperties)this.mFigurePropertyHistoryList.get(this.mFigureStateCounter);
		this.setProperties(p);

		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);
			}
		}

	}



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


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


	/**
	 * 
	 */
	protected boolean mChangedFlag = false;

	
	/**
	 * 
	 */
	public void setChanged( final boolean b )
	{
		this.mChangedFlag = 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;
		}
		else
		{
			return this.changeCursor();
		}
	}



	/**
	 * マウス位置を属性に設定
	 */
	private void setMouseLocation( final int x, final int y )
	{

		final float radius = 1.50f*SGDrawingWindow.getAnchorSize();

		final Rectangle2D rect = this.getGraphRect();


		// 左上
		if( ( Math.abs(x-rect.getX()) < radius ) && ( Math.abs(y-rect.getY()) < radius ) )
		{
			mMouseLocation = NORTH_WEST;
			return;
		}
		// 右下
		else if( ( Math.abs( x-(rect.getX()+rect.getWidth() ) ) < radius )
			&& ( ( Math.abs( y-(rect.getY()+rect.getHeight() ) ) < radius ) ) )
		{
			mMouseLocation = SOUTH_EAST;
			return;
		}
		// 右上
		else if( ( Math.abs(x-(rect.getX()+rect.getWidth())) < radius )
			&& ( Math.abs(y-rect.getY()) < radius ) )
		{
			mMouseLocation = NORTH_EAST;
			return;
		}
		// 左下
		else if( ( Math.abs(x-rect.getX()) < radius )
			&& ( ( Math.abs(y-(rect.getY()+rect.getHeight())) < radius ) ) )
		{
			mMouseLocation = SOUTH_WEST;
			return;
		}
		// 左端中央
		else if( ( Math.abs(x-rect.getX()) < radius )
			&& ( Math.abs(y-(rect.getY()+rect.getHeight()/2)) < radius ) )
		{
			mMouseLocation = WEST;
			return;
		}
		// 右端中央
		else if( ( Math.abs(x-(rect.getX()+rect.getWidth())) < radius )
			&& ( Math.abs(y-(rect.getY()+rect.getHeight()/2)) < radius ) )
		{
			mMouseLocation = EAST;
			return;
		}
		// 上端中央
		else if( ( Math.abs(y-rect.getY()) < radius )
			&& ( Math.abs(x-(rect.getX()+rect.getWidth()/2)) < radius ) )
		{
			mMouseLocation = NORTH;
			return;
		}
		// 下端中央
		else if( ( Math.abs(y-(rect.getY()+rect.getHeight())) < radius )
			&& ( Math.abs(x-(rect.getX()+rect.getWidth()/2)) < radius ) )
		{
			mMouseLocation = SOUTH;
			return;
		}
		else
		{
			mMouseLocation = OTHER;
			return;
		}

	}



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

		// マウスの位置に応じてカーソルを変更
		Cursor cur = null;
		switch( 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.getWindow().setCursor( cur );
	}



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

		this.mWnd.setCursor( 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 );
		

		// 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();


		// grid
//		item = SGUtility.addCheckBoxItem( p, this, MENUCMD_GRID_VISIBLE );
//		item.setSelected( SGDefaultValues.FIGURE_GRID_VISIBLE );
//
//		SGUtility.addItem( p, this, MENUCMD_GRID_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.mAxisElement.getSpaceAxisLineAndNumber() );
		fp.setSpaceNumberAndTitle( this.mAxisElement.getSpaceNumberAndTitle() );
		fp.setBackgroundColor( this.mBackgroundColor );
		fp.setTransparent( this.mTransparentFlag );
		fp.setLegendVisible( this.mLegendElement.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( 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_GRID_VISIBLE ) )
			{
				final boolean b = this.mGridElement.isGridVisible();
				this.mGridElement.setGridVisible(!b);
				this.mGridElement.setChanged(true);
				this.updatePopupMenu();
				this.repaint();
				this.notifyToRoot();
				return;
			}
			else if( command.equals( MENUCMD_CUT ) )
			{
				this.mWnd.cutFocusedObjects();
			}
			else if( command.equals( MENUCMD_COPY ) )
			{
				this.mWnd.copyFocusedObjects();
			}
			else if( command.equals( MENUCMD_PASTE ) )
			{
				this.mWnd.pasteCopiedObjects();
			}
			else if( command.equals( MENUCMD_DELETE ) )
			{
				this.mWnd.deleteFocusedObjects();
			}
			else if( command.equals( MENUCMD_DUPLICATE ) )
			{
				this.mWnd.duplicateFocusedObjects();
			}

		}



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

		if( source instanceof SGIFigureElement )
		{

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

			// SGIFigureElementの変更を伝える場合
			if( command.equals( SGFigure.NOTIFY_CHANGE ) )
			{
				// データの同期を取る
//					this.mergeData( element );

				// broadcastして同期を取る
				this.broadcast( element );
			}
			// 共通プロパテイ設定
			else if( command.equals( SGFigure.SET_PROPERTIES_OF_SELECTED_OBJECTS ) )
			{
				//
				this.mWnd.showPropertyDialogForSelectedObjects( this, element );
			}
			else if( command.equals( SGFigure.CLEAR_FOCUSED_OBJECTS ) )
			{
				this.mWnd.clearAllFocusedObjectsInFigures();
			}
			//
			else if( command.equals( SGFigure.NOTIFY_CHANGE_TO_ROOT ) )
			{
				this.notifyToRoot();
			}
			else if( command.equals( SGFigure.NOTIFY_CHANGE_CURSOR ) )
			{
				this.setCursorToWindow(element);
			}

		}


		this.repaint();

		return;

	}


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

		for( int ii=0; ii<this.mDataList.size(); ii++ )
		{
			final SGData data = (SGData)this.mDataList.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;

	}





	/**
	 * 
	 * @return
	 */
	private ArrayList mFigurePropertyHistoryList = new ArrayList();



	/**
	 * 
	 * @return
	 */
	private int mFigureStateCounter = 0;



	/**
	 * 
	 */
	public boolean initPropertiesHistory()
	{
		this.addFigurePropertyHistory( (FigureProperties)this.getProperties() );
		return true;
	}



	/**
	 * 
	 * @return
	 */
	private boolean addFigurePropertyHistory( FigureProperties p )
	{

//System.out.println("<< addFigurePropertyHistory >>");
//System.out.println(this.mFigureStateCounter);

		ArrayList list = new ArrayList();
		for( int ii=0; ii<this.mFigureStateCounter; ii++ )
		{
			list.add( this.mFigurePropertyHistoryList.get(ii) );
		}
		list.add(p);

		this.mFigurePropertyHistoryList = list;

//System.out.println(this.mFigurePropertyHistoryList);
//System.out.println("x="+p.x+"  y="+p.y);
//System.out.println("w="+p.width+"  h="+p.height);
//System.out.println();

		return true;
	}




	/**
	 * アンドゥ対象オブジェクトの履歴リスト
	 */
	protected ArrayList mUndoableObjectHistoryList = new ArrayList();



	/**
	 * アンドゥ操作の実行
	 */
	public boolean onUndo()
	{

//System.out.println("<< SGFigure : onUndo >>");


		// 記録されているundo操作対象オブジェクトの取得
		if( this.mCurrentStateCounter == 0 )
		{
//System.out.println("return false");
			return false;
		}

		ArrayList objList = (ArrayList)this.mUndoableObjectHistoryList.get(
			this.mCurrentStateCounter - 1 );
		for( int ii=0; ii<objList.size(); ii++ )
		{
			SGIUndoable obj = (SGIUndoable)objList.get(ii);
			// アンドゥ操作の実行の依頼
			boolean flag;
			if( obj.equals(this) )
			{
				flag = this.undo();
			}
			else
			{
				flag = obj.onUndo();
			}
		
			if( !flag )
			{
//System.out.println("return false");
				return false;
			}
		}


		// decrement
		this.mCurrentStateCounter--;


//System.out.println("-- history --");
//System.out.println(this.mUndoableObjectHistoryList);
//System.out.println(this.mFigurePropertyHistoryList);
//System.out.println("cnt="+this.mCurrentStateCounter);
//System.out.println();


		return true;

	}


	/**
	 * 
	 */
	public boolean onRedo()
	{

//System.out.println("<< onRedo >>");

		// 記録されているundo操作対象オブジェクトの取得
		if( this.mCurrentStateCounter == this.mUndoableObjectHistoryList.size() )
		{
			return false;
		}


		ArrayList objList = (ArrayList)this.mUndoableObjectHistoryList.get( this.mCurrentStateCounter );
		for( int ii=0; ii<objList.size(); ii++ )
		{
			SGIUndoable obj = (SGIUndoable)objList.get(ii);
			// リドゥ操作の実行の依頼
			boolean flag;
			if( obj.equals(this) )
			{
				flag = this.redo();
			}
			else
			{
				flag = obj.onRedo();
			}

			if( !flag )
			{
//System.out.println("return false");
				return false;
			}
		}


		// increment
		this.mCurrentStateCounter++;


		return true;

	}



	/**
	 * 
	 * @return
	 */
	public boolean commit()
	{
		this.repaint();

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

		this.mTemporaryProperties = null;

		return true;
	}


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

		return true;
	}


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

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

		this.mTemporaryProperties = null;

		return true;
	}


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

		ArrayList changedObjList = new ArrayList();
		if( this.mChangedFlag )
		{
			this.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() )
			{
//System.out.println(element);

				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.updateThisObjectHistory();
						changedObjList.add(this);
						this.setChanged(false);
					}
				}
			}
		}

		if( changedObjList.size()!=0 )
		{
//System.out.println(changedObjList);
			this.updateObjectHistory( changedObjList );
		}

		return true;

	}


	/**
	 * 
	 * @return
	 */
	public boolean updateThisObjectHistory()
	{
		this.mFigureStateCounter++;
		this.addFigurePropertyHistory( (FigureProperties)this.getProperties() );
		return true;
	}



	/**
	 * オブジェクトの履歴を更新する
	 * @return
	 */
	public boolean updateObjectHistory( final SGIUndoable obj )
	{

		ArrayList objList = new ArrayList();
		objList.add(obj);
		boolean flag = this.updateObjectHistory(objList);
		if( !flag )
		{
			return false;
		}

		return true;

	}



	/**
	 * オブジェクトの履歴を更新する
	 */
	public boolean updateObjectHistory( final ArrayList objList )
	{

		ArrayList list = new ArrayList();
		for( int ii=0; ii<this.mCurrentStateCounter; ii++ )
		{
			Object obj = this.mUndoableObjectHistoryList.get(ii);
			list.add(obj);
		}
		list.add( new ArrayList(objList) );

		this.mUndoableObjectHistoryList = list;
		this.mCurrentStateCounter++;

		return true;
	}



	/**
	 * 現在、状態が履歴のどの位置にあるのかを示すカウンタ
	 * 自身と、属性のSGIFigureElementの状態変更において変更される
	 */
	private int mCurrentStateCounter = 0;



	/**
	 * 
	 * @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 bottomMax = topAndBottom.y;
		float leftMax = leftAndRight.x;
		float rightMax = leftAndRight.y;


		// 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 )
	{
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii].synchronize(element);
		}
		return true;
	}



	/**
	 * 
	 */
	public boolean removeData( final SGData data )
	{
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=this.mDataList.size()-1; ii>=0; ii-- )
		{
			final SGData data_ = (SGData)this.mDataList.get(ii);
			if( data_.equals(data) )
			{
				this.mDataList.remove(ii);
				for( int jj=0; jj<array.length; jj++ )
				{
					array[jj].removeData(data);
				}

				return true;
			}
		}

		return true;
	}



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



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



	/**
	 * 描画メソッド
	 */
	public void paintComponent(final Graphics g)
	{
		super.paintComponent(g);
		this.paintGraphics(g,true);
	}


/*
static long time = 0;
	public void paint( Graphics g )
	{
		super.paint(g);
//		Graphics2D g2d = (Graphics2D)g;


final long s = System.currentTimeMillis();
System.out.println(s-time);
time = s;
//System.out.println();
	}
*/


	/**
	 * draw the figure
	 * @param g2d
	 */
	public void paintGraphics( Graphics g, boolean clip )
	{
		Graphics2D g2d = (Graphics2D)g;
		
		// 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 );
			}
		}

	}



	/**
	 * 
	 * @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 SGIGraphElement )
			{
				continue;
			}
			else
			{
				if( el==null )
				{
					return false;
				}
				parent.appendChild( el );
			}
		}

		ArrayList elList = new ArrayList();
		if( this.mGraphElement.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 = 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_ ) + SGUtilityNumber.cm );
		el.setAttribute( KEY_FIGURE_Y_IN_CLIENT, Float.toString( y_ ) + SGUtilityNumber.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 ) + SGUtilityNumber.cm );
		el.setAttribute( KEY_FIGURE_HEIGHT, Float.toString( h ) + SGUtilityNumber.cm );

		el.setAttribute( KEY_SPACE_AXIS_LINE_AND_NUMBER, Float.toString( this.getSpaceAxisLineAndNumber()*ratio ) + SGUtilityNumber.cm );
		el.setAttribute( KEY_SPACE_NUMBER_AND_TITLE, Float.toString( this.getSpaceNumberAndTitle()*ratio ) + SGUtilityNumber.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;
	}
	
	
	/**
	 *
	 */
	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.mGraphElement.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.mGraphElement.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.mGraphElement.getDataName(data);
	}


	/**
	 * 
	 * @param name
	 * @param data
	 * @return
	 */
	public boolean setDataName( String name, SGData data )
	{
		return this.mGraphElement.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 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 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;
	}

}

