
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.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
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, ComponentListener, WindowListener,
		SGIConstants, SGIUndoable, SGIMovable, SGISelectable
{


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

	
	/**
	 * 倍率100%のときの、クライアント領域におけるグラフ描画領域のX座標
	 */
	protected float mGraphRectX = 0.0f;


	/**
	 * 倍率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 JDialog 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;


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


	/**
	 * 
	 */
	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 = false;


	/**
	 * 
	 */
	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_TIMING_LINE = 10;
	public static final int LAYER_GRAPH = 20;
	public static final int LAYER_SIGNIFICANT_DIFFERENCE = 30;
	public static final int LAYER_AXIS = 40;
	public static final int LAYER_AXIS_BREAK = 50;
	public static final int LAYER_LEGEND = 60;
	public static final int LAYER_STRING = 70;


	/**
	 * 
	 */
	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_PROPERTY_OF_SELECTED_DATA = "Set properties of selected data";


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



	/**
	 * 
	 */
	public boolean setLegendVisible( boolean flag )
	{
		final boolean v = this.mLegendElement.isLegendVisible();
		if( v!=flag )
		{
			this.mLegendElement.setLegendVisible( flag );
			this.mLegendElement.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;
	}


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

	
	/**
	 * 
	 */
	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;
	}
	
	
	/**
	 * 
	 */
	public SGIFigureElement[] getIFigureElementArray()
	{
		SGIFigureElement[] array = new SGIFigureElement[7];

		array[0] = this.mAxisElement;
		array[1] = this.mGraphElement;
		array[2] = this.mLegendElement;
		array[3] = this.mStringElement;
		array[4] = this.mSignificantDifferenceElement;
		array[5] = this.mAxisBreakElement;
		array[6] = this.mTimingLineElement;

		return array;
	}



	/**
	 * 
	 */
	public SGIFigureElement[] getIFigureElementArrayFromLayer()
	{
		SGIFigureElement[] array = new SGIFigureElement[7];

		array[0] = this.mStringElement;
		array[1] = this.mLegendElement;
		array[2] = this.mAxisBreakElement;
		array[3] = this.mAxisElement;
		array[4] = this.mSignificantDifferenceElement;
		array[5] = this.mGraphElement;
		array[6] = this.mTimingLineElement;

		return array;
	}



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



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



	/**
	 * 
	 */
	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.getClientRect();
		return (float)cRect.getX() + this.mMagnification*this.mGraphRectX;
	}



	/**
	 * 
	 * @return
	 */
	public float getGraphRectY()
	{
		final Rectangle2D cRect = this.mWnd.getClientRect();
		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 boolean setGraphRectLocation(
		final float x, final float y )
	{
		this.setGraphRectLocation2(x,y);
		this.setGraphRectToFigureElement();
		return true;
	}



	/**
	 * 引数はピクセル単位の座標値
	 */
	private boolean setGraphRectLocation2(
		final float x, final float y )
	{
		final Rectangle2D cRect = this.mWnd.getClientRect();
		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.getClientRect();

		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;

		return true;
	}



	/**
	 * 
	 */
	public boolean setGraphRectSize(
		final float w,
		final float h )
	{
		this.setGraphRectSize2(w,h);
		this.setGraphRectToFigureElement();
		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.setGraphRectToFigureElement();
		return true;
	}



	/**
	 * 
	 * @return
	 */
	protected boolean setGraphRectToFigureElement()
	{

		// SGIFigureElementにも属性設定
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii]==null )
			{
				continue;
			}
		
			array[ii].setGraphRect(
				this.getGraphRectX(),
				this.getGraphRectY(),
				this.getGraphRectWidth(),
				this.getGraphRectHeight()
			);
		}
		return true;
	}




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

/*
			if( array[ii] instanceof SGIGraphElement )
			{
				SGIGraphElement g = (SGIGraphElement)array[ii];
//				g.addData( data, nameNew, p );
			}
			else
			{
				array[ii].addData( data, nameNew );
			}
*/		}

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

		//
		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 );
		}
		if( this.setGraphRectToFigureElement() == false )
		{
			return false;
		}


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

		return true;
	}



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



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

		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.mAxisElement.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 )
		{
			// set invisible symbols around all objects of SGIFigureElement objects
//			this.setSymbolsVisibleAroundAllObjects(false);

			//
			this.mWnd.setFocusedFigure( this, false );
			
			// Neither CTRL key nor SHIFT key is pressed.
			if( ( ( mod & MouseEvent.CTRL_MASK ) == 0 )
					& ( ( mod & MouseEvent.SHIFT_MASK ) == 0 ) )
			{
/*				// clear all selected objects other than present selected object
				ArrayList list = this.mWnd.getVisibleFigureListFromMap();
				for( int ii=0; ii<list.size(); ii++ )
				{
					SGFigure figure = (SGFigure)list.get(ii);
					if( figure!=this )
					{
						figure.setSelected(false);
					}
				}
*/

/*
				SGIFigureElement[] array = this.getIFigureElementArray();
				for( int ii=0; ii<array.length; ii++ )
				{
					if( array[ii]!=el )
					{
						array[ii].clearFocusedObjects();
					}
				}
*/
				// clear all focused figures
//				this.mWnd.clearFocusedFigures();

			}

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

			// set visible symbols around all objects of SGIFigureElement objects
//			this.setSymbolsVisibleAroundAllObjects(true);

			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.getDialog().setVisible(true);
			}

			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.mRubberBandVisibleFlag );
				}
				else if( com.equals( MENUCMD_SHOW_BOUNDING_BOX ) )
				{
					item.setSelected( SGFigure.mBoundingBoxVisibleFlag );
				}
			}
		}

		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 )
	{
		// check whether the button is pressed on an effective point
		if( this.mMouseLocation != OTHER )
		{
			return this.pressed(e);
		}
		
		// ask to SGIFigureElement objects
		SGIFigureElement el = this.onFigureElementPressed(e);
		if( el!=null )
		{
			// aftertreatment 
			this.afterPressed(e);
			return true;
		}
		
		// check whether the button is pressed in the rectangle
		if( this.isGraphRectContains( e.getPoint() ) )
		{
			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 )
	{


		// ドラッグ中の矩形の初期設定
		this.mWnd.setDraggingRectOfFocusedFigures();


		// Change the cursor.
		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.updateFigureRect();


		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
	 */
	protected boolean updateFigureRect()
	{
		this.mTempFigureRect.setRect( this.getGraphRectInClientRect() );
		return true;
	}



	/**
	 * ドラッグ中の矩形を設定
	 */
	protected boolean setRubberBandRect()
	{
		this.mRubberBandRect.setRect(
			this.getGraphRectX(),
			this.getGraphRectY(),
			this.getGraphRectWidth(),
			this.getGraphRectHeight()
		);

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

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


		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;



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

		if( mMouseLocation == NORTH )
		{
//System.out.println("NORTH");
			pos.setLocation(
				pos.getX(),
				pos.getY() + diffY
			);
			sizeNewY = sizeOldY - diffY;
			y = yOld + sizeOldY - sizeNewY;
			h = sizeNewY;
		}
		else if( mMouseLocation == SOUTH )
		{
//System.out.println("SOUTH");
			pos.setLocation(
				pos.getX(),
				pos.getY() + diffY
			);
			sizeNewY = sizeOldY + diffY;
			h = sizeNewY;
		}
		else if( mMouseLocation == WEST )
		{
//System.out.println("WEST");
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY() + diffY
			);
			sizeNewX = sizeOldX - diffX;
			x = xOld + sizeOldX - sizeNewX;
			w = sizeNewX;
		}
		else if( mMouseLocation == EAST )
		{
//System.out.println("EAST");
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY()
			);
			sizeNewX = sizeOldX + diffX;
			w = sizeNewX;
		}
		else if( mMouseLocation == 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(
			( mMouseLocation == 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(
			( mMouseLocation == 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(
			( mMouseLocation == 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(
			( mMouseLocation == 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(
			( mMouseLocation == 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(
			( mMouseLocation == 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.mRubberBandRect.setRect( x, y, w, h );


		// ドラッグ中の線を描画しない場合には、直ちにフィギュアに変更を反映する
		if( SGFigure.mRubberBandVisibleFlag == false )
		{
			// 矩形をもとにフィギュア境界を更新
			final boolean flag = this.setGraphRectOnDragging();
			if( !flag )
			{
				// 矩形を元に戻す
				this.mRubberBandRect.setRect( xOld, yOld, wOld, hOld );
			}
		}


		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 int dx, final int dy )
	{
		// set the location of the rectangle for dragging
		Rectangle2D dRect = this.getRubberBandRect();
		dRect.setRect(
			dRect.getX() + dx,
			dRect.getY() + dy,
			dRect.getWidth(),
			dRect.getHeight()
		);

		// if the rectangle for dragging is invisible,
		// set the graph area rectangle immediately
		if( SGFigure.mRubberBandVisibleFlag == 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);
		}
	}
	
	

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

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

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

		return true;
	}



	/**
	 * 
	 * @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.mRubberBandVisibleFlag )
		{
			this.setGraphRectOnDragging();
		}


		// SGIFigureElementへの処理
		if( mPressedElement != null )
		{
			mPressedElement.onMouseReleased(e);
/*			SGIFigureElement[] array = this.getIFigureElementArray();
			for( int ii=0; ii<array.length; ii++ )
			{
				array[ii].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;
	}



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

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

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


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


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


		// set the dragging rectangle
		this.setRubberBandRect();

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





	/**
	 * 
	 */
	public void componentHidden(final ComponentEvent e){}



	/**
	 * 
	 */
	public void componentResized(final ComponentEvent e){}



	/**
	 * 
	 */
	public void componentShown(final ComponentEvent e){}



	/**
	 * 
	 */
	public void componentMoved(final ComponentEvent e){}



	/**
	 * 
	 */
	public void windowActivated(final WindowEvent e)
	{
	}


	/**
	 * 
	 */
	public void windowDeactivated(final WindowEvent e)
	{
	}


	/**
	 * 
	 */
	public void windowIconified(final WindowEvent e)
	{
	}


	/**
	 * 
	 */
	public void windowDeiconified(final WindowEvent e)
	{
	}


	/**
	 * 
	 */
	public void windowOpened(final WindowEvent e)
	{
	}


	/**
	 * 
	 */
	public void windowClosed(final WindowEvent e)
	{
	}


	/**
	 * 
	 */
	public void windowClosing(final WindowEvent e)
	{
		Object obj = e.getSource();

		// フィギュアのプロパティ設定用ダイアログ
		if( obj.equals( this.mDialog ) )
		{
			this.onCanceled();
		}

	}



	/**
	 * 
	 * @return
	 */
	public boolean undo()
	{
		this.mFigureStateCounter--;

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

		this.setGraphRectToFigureElement();

		return true;
	}



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

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

		this.setGraphRectToFigureElement();

		return true;
	}



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


	/**
	 * 
	 */
	protected Rectangle2D getRubberBandRect()
	{
		return this.mRubberBandRect;
	}



	/**
	 * 
	 * @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.changeCursor();
			}
		}
		else
		{
			return this.changeCursor();
		}

		return null;
	}



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

		final float radius = 1.50f*SGDrawingWindow.mAnchorSize;

		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.getWindow().setCursor( cur );

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

		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";



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

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


		// draw rectangle
		SGUtility.addItem( p, this, MENUCMD_RUBBER_BANDING );

		// show bounding box
		SGUtility.addItem( p, this, MENUCMD_SHOW_BOUNDING_BOX );

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




	/**
	 * 
	 */
	private JDialog getDialog()
	{

		final SGFigureDialog dg = (SGFigureDialog)this.mDialog;

		// set the location
		dg.setLocation( this.mWnd.getLocation() );


		// ボタンに装飾
		dg.setColorButtonBorder(true);


		// ダイアログに設定
		this.setDialogProperty();


		// テンポラリ・オブジェクトを作成
		this.mTemporaryProperties = this.getProperties();


		return this.mDialog;

	}



	/**
	 * 
	 */
	private boolean setDialogProperty()
	{

		final SGFigureDialog dg = (SGFigureDialog)this.mDialog;

		// Added the ID-number to the title of the dialog.
		String title = SGFigureDialog.TITLE + " : " + this.getID();
		this.mDialog.setTitle(title);


		// SGFigure
		dg.setFigureX( this.mGraphRectX*SGIConstants.CM_POINT_RATIO );
		dg.setFigureY( this.mGraphRectY*SGIConstants.CM_POINT_RATIO );
		dg.setFigureWidth( this.mGraphRectWidth*SGIConstants.CM_POINT_RATIO );
		dg.setFigureHeight( this.mGraphRectHeight*SGIConstants.CM_POINT_RATIO );

		dg.setFigureBackgroundColor( this.mBackgroundColor );

		dg.setFigureTransparentFlag( this.mTransparentFlag );

		dg.setFigureSpaceLineAndNumber( this.getSpaceAxisLineAndNumber()*SGIConstants.CM_POINT_RATIO/this.mMagnification );
		dg.setFigureSpaceNumberAndTitle( this.getSpaceNumberAndTitle()*SGIConstants.CM_POINT_RATIO/this.mMagnification );

		// SGILegendElement
		dg.setFigureLegendVisible( this.mLegendElement.isLegendVisible() );

		return true;
	}



	/**
	 * 
	 */
	private boolean createDialog()
	{

		final SGFigureDialog dg = new SGFigureDialog( this.mWnd, true );


		// add action listener of OK,Cancel,Preview buttons
		// add window listener
		dg.setListener(this);

		this.mDialog = dg;


		return true;

	}


	/**
	 * 
	 */
	private boolean setPropertyWithDialog()
	{
//System.out.println("Figure: setPropertyWithDialog");

		// get the properties from dialog
		FigureProperties p = this.getPropertyFromDialog();

		this.setProperties(p);

		this.setGraphRectToFigureElement();

//		this.repaint();

		return true;

	}




	/**
	 * 
	 */
	public SGProperties getProperties()
	{
		final FigureProperties p = new FigureProperties();

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

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

		// space
//		p.spaceLineAndNumber = this.mSpaceAxisLineAndNumber;
//		p.spaceNumberAndTitle = this.mSpaceNumberAndTitle;

		p.spaceLineAndNumber = this.mAxisElement.getSpaceAxisLineAndNumber();
		p.spaceNumberAndTitle = this.mAxisElement.getSpaceNumberAndTitle();
		
		// legend
		p.legendVisible = this.mLegendElement.isLegendVisible();

		// background color
		p.bgColor = this.mBackgroundColor;

		// transparency
		p.transparent = this.mTransparentFlag;

		return p;
	}






	/**
	 * 
	 */
	private FigureProperties getPropertyFromDialog()
	{
		final SGFigureDialog dg = (SGFigureDialog)this.mDialog;
		final FigureProperties p = dg.getProperties();
		return p;
	}



	/**
	 * アクションが発生すると呼び出されます。
	 */
	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.repaint();
				return;
			}
			else if( command.equals( MENUCMD_RUBBER_BANDING ) )
			{
				SGFigure.mRubberBandVisibleFlag = !SGFigure.mRubberBandVisibleFlag;
				this.repaint();
				return;
			}
			else if( command.equals( MENUCMD_SAVE_PROPERTY ) )
			{
				this.mWnd.createPropertyFileFromFocusedFigures();
			}
			else if( command.equals( MENUCMD_PROPERTY ) )
			{
				this.getDialog().setVisible(true);
			}
			else if( command.equals( MENUCMD_MOVE_TO_FRONT ) )
			{
				this.mWnd.moveFocusedObjectsToFront();
//				this.mWnd.moveToFront(this);
			}
			else if( command.equals( MENUCMD_MOVE_TO_BACK ) )
			{
				this.mWnd.moveFocusedObjectsToBack();
//				this.mWnd.moveToBack(this);
			}
			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();
			}


			//
			// dialog
			//

			SGFigureDialog dg = (SGFigureDialog)this.mDialog;
			ArrayList comList = dg.getActiveComponentList();

			if( command.equals("OK") || comList.contains(source) )
			{
				this.onOK();
			}
			else if( command.equals("Cancel") )
			{
				this.onCanceled();
			}
			else if( command.equals("Preview") )
			{
				this.onPreviewed();
			}

		}



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

		if( source instanceof SGIFigureElement )
		{

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

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

				// broadcastして同期を取る
				this.notifyFigureElement( element );
			}
			// 共通プロパテイ設定
			else if( command.equals( SGFigure.SET_PROPERTY_OF_SELECTED_DATA ) )
			{
				//
				this.mWnd.setPropertyOfSelectedObjects();
			}
			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
	 */
	public boolean notifyFigureElement( SGIFigureElement element )
	{
		this.broadcast(element);
//		this.calcElementBounds();

		this.repaint();

		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;

	}



	/**
	 * 
	 */
	protected boolean onOK()
	{

		boolean flag;

		// ダイアログからプロパティを取得して設定
		flag = this.setPropertyWithDialog();
		if( !flag )
		{
			return false;
		}


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


		this.mTemporaryProperties = null;
		this.mDialog.setVisible(false);
		this.repaint();


		this.notifyToRoot();

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





	/**
	 * 
	 */
	protected boolean onCanceled()
	{

		if( this.setProperties( this.mTemporaryProperties ) == false )
		{
			return false;
		}

		this.setGraphRectToFigureElement();

		this.mTemporaryProperties = null;
		this.mDialog.setVisible(false);
		this.repaint();

		return true;
	}


	/**
	 * 
	 */
	protected boolean onPreviewed()
	{
		this.setPropertyWithDialog();
		this.repaint();

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


		topAndBottom.x = topMax;
		topAndBottom.y = bottomMax;
		leftAndRight.x = leftMax;
		leftAndRight.y = rightMax;

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




	/**
	 * 
	 * @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);
		final Graphics2D g2d = (Graphics2D) g;
		this.paintGraphics2D(g2d);
	}


/*
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 paintGraphics2D( final Graphics2D g2d )
	{
		// 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/this.mMagnification ) + SGUtilityNumber.cm );
		el.setAttribute( KEY_SPACE_NUMBER_AND_TITLE, Float.toString( this.getSpaceNumberAndTitle()*ratio/this.mMagnification ) + 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 static final int MINIMAL_LENGTH_ORDER = -1;


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

		FigureProperties fp = (FigureProperties)p;

		// if the difference of the coordinate or size is larger than the minimal length,
		// set the property
		final float diffX = Math.abs( this.mGraphRectX - fp.x )*SGIConstants.CM_POINT_RATIO;
		final float diffY = Math.abs( this.mGraphRectY - fp.y )*SGIConstants.CM_POINT_RATIO;
		final float diffW = Math.abs( this.mGraphRectWidth - fp.width )*SGIConstants.CM_POINT_RATIO;
		final float diffH = Math.abs( this.mGraphRectHeight - fp.height )*SGIConstants.CM_POINT_RATIO;
		final float eps = (float)SGUtilityNumber.getPowersOfTen(MINIMAL_LENGTH_ORDER);
		if( diffX > eps )
		{
			this.mGraphRectX = fp.x;
		}
		if( diffY > eps )
		{
			this.mGraphRectY = fp.y;
		}
		if( diffW > eps )
		{
			this.mGraphRectWidth = fp.width;
		}
		if( diffH > eps )
		{
			this.mGraphRectHeight = fp.height;
		}
		this.setBackgroundColor(fp.bgColor);
		this.setTransparent(fp.transparent);

		// set to the axis
		this.setSpaceAxisLineAndNumber( fp.spaceLineAndNumber*this.mMagnification );
		this.setSpaceNumberAndTitle( fp.spaceNumberAndTitle*this.mMagnification );

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

		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
	{
		float x;
		float y;
		float width;
		float height;
		float spaceLineAndNumber;
		float spaceNumberAndTitle;
		Color bgColor;
		boolean transparent;
		boolean legendVisible;

		public FigureProperties()
		{
			super();
		}

		public String toString()
		{
			String str = new String("[");
			str += new String("x="+x+", ");
			str += new String("y="+y+", ");
			str += new String("width="+width+", ");
			str += new String("height="+height+", ");
			str += new String("spaceLineAndNumber="+spaceLineAndNumber+", ");
			str += new String("spaceNumberAndTitle="+spaceNumberAndTitle+", ");
			str += new String("background color="+bgColor+", ");
			str += new String("transparent="+transparent+", ");
			str += new String("legend visible="+legendVisible+", ");
			str += new String("]");

			return str;
		}

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

			FigureProperties p = (FigureProperties)obj;

			if( p.x!=this.x ) return false;
			if( p.y!=this.y ) return false;
			if( p.width!=this.width ) return false;
			if( p.height!=this.height ) return false;
			if( p.spaceLineAndNumber!=this.spaceLineAndNumber ) return false;
			if( p.spaceNumberAndTitle!=this.spaceNumberAndTitle ) return false;
			if( p.bgColor.equals(this.bgColor) == false) return false;
			if( p.transparent!=this.transparent ) return false;
			if( p.legendVisible!=this.legendVisible ) return false;

			return true;

		}


	}

	


}
