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

import java.util.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;


/**
 * A figure.
 */

public abstract class SGFigure extends JLayeredPane
	implements MouseListener, MouseMotionListener, ActionListener,
		ComponentListener, WindowListener, SGIUndoable
{


	/**
	 * ID-number of this figure.
	 */
	protected 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;


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


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


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


	/**
	 * 倍率100%のときの、グラフ描画領域の高さ
	 */
	protected float mGraphAreaHeight = 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 Rectangle2D mTempFigureRect = new Rectangle2D.Float();


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


	/**
	 * 
	 */
	protected Color mBackgroundColor = new Color( 255, 210, 210 );


	/**
	 * 
	 */
	protected Rectangle2D mRectangleOnDragging = new Rectangle2D.Float();


	/**
	 * 
	 */
	public static boolean mDrawRectangleOnDraggingFlag = false;


	/**
	 * 
	 */
	public static boolean mDraggingFlag = 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;


	/**
	 * 一時的に保存しておくデータのリスト
	 */
	protected ArrayList mTemporaryDataList = null;



	/**
	 * 
	 */
	private float mSpaceAxisLineAndNumber = SGDefaultValues.SPACE_AXIS_LINE_AND_NUMBER;


	/**
	 * 
	 */
	private float mSpaceNumberAndTitle = SGDefaultValues.SPACE_NUMBER_AND_TITLE;



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



	/**
	 * 
	 */
	public static final int LAYER_GRAPH = 10;


	/**
	 * 
	 */
	public static final int LAYER_AXIS = 20;


	/**
	 * 
	 */
	public static final int LAYER_LEGEND = 30;


	/**
	 * 
	 */
	public static final int LAYER_STRING = 40;


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


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


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


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


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


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


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


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


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



	/**
	 * 
	 */
	public static final int NOTIFY_CHANGE = 0;


	/**
	 * 
	 */
	public static final int UPDATE_HISTORY = 1;


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



	/**
	 * 
	 */
	protected final String SHOW_FRAME = "show/hide frame lines";


	/**
	 * 
	 */
	protected final String ADD_STRING = "add a string";


	/**
	 * 
	 */
	protected final String REMOVE_FIGURE = "Remove Selected Figures";


	/**
	 * 
	 */
	public final String ERRMSG_FAILED_TO_CREATE_DRAWING_ELEMENTS
		= "描画要素の作成に失敗しました。";




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



	/**
	 * Constructor.
	 * @param figureID the ID-number of this figure
	 * @param wnd ths drawing 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();

		this.addMouseListener(this);
		this.addMouseMotionListener(this);

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

		return true;
	}


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


	/**
	 * 
	 */
	protected boolean isFocusedFigure()
	{
		final ArrayList fList = this.mWnd.mFocusedFigureList;
		final boolean flag = fList.contains(this);

		return flag;
	}






//
// set/getメソッド
//


	/**
	 *
	 */
	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 boolean setSpaceAxisLineAndNumber( final float space )
	{
		this.mSpaceAxisLineAndNumber = space;
		this.mAxisElement.setSpaceAxisLineAndNumber( space );

		return true;
	}


	/**
	 * 
	 * @return
	 */
	public boolean setSpaceNumberAndTitle( final float space )
	{
		this.mSpaceNumberAndTitle = space;
		this.mAxisElement.setSpaceNumberAndTitle( space );

		return true;
	}



	/**
	 * 
	 */
	public boolean setLegendVisible( boolean flag )
	{
		this.mLegendElement.setLegendVisible( flag );

		return true;
	}



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



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

		return true;
	}


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


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


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


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


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

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

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

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



	/**
	 * 
	 */
	public SGIFigureElement[] getIFigureElementArray()
	{
		SGIFigureElement[] array = new SGIFigureElement[4];

		array[0] = this.mAxisElement;
		array[1] = this.mGraphElement;
		array[2] = this.mLegendElement;
		array[3] = this.mStringElement;

		return array;
	}


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



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



	/**
	 * 
	 */
	public Rectangle2D getGraphAreaRect()
	{
		final Rectangle2D rect = new Rectangle2D.Float(
			this.getGraphAreaX(),
			this.getGraphAreaY(),
			this.getGraphAreaWidth(),
			this.getGraphAreaHeight() );

		return rect;
	}



	/**
	 * 
	 * @return
	 */
	public float getGraphAreaX()
	{
		final Rectangle2D cRect = this.mWnd.getClientRect();
		return (float)cRect.getX() + this.mMagnification*this.mGraphAreaX;
	}



	/**
	 * 
	 * @return
	 */
	public float getGraphAreaY()
	{
		final Rectangle2D cRect = this.mWnd.getClientRect();
		return (float)cRect.getY() + this.mMagnification*this.mGraphAreaY;
	}



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



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




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



	/**
	 * 引数はピクセル単位の座標値
	 */
	private boolean setGraphAreaLocation2(
		final float x, final float y )
	{
		final Rectangle2D cRect = this.mWnd.getClientRect();
		this.mGraphAreaX = ( x - (float)cRect.getX() )/this.mMagnification;
		this.mGraphAreaY = ( y - (float)cRect.getY() )/this.mMagnification;

		return true;
	}



	/**
	 * 
	 */
	public boolean setGraphAreaSize(
		final float w,
		final float h )
	{
		this.setGraphAreaSize2(w,h);
		this.setGraphAreaRectToFigureElement();
		return true;
	}


	/**
	 * 
	 */
	private boolean setGraphAreaSize2(
		final float w,
		final float h )
	{
		this.mGraphAreaWidth = w/this.mMagnification;
		this.mGraphAreaHeight = h/this.mMagnification;
		return true;
	}



	/**
	 * 
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @return
	 */
	public boolean setGraphAreaRect(
		final float x, final float y, final float w, final float h )
	{
		this.setGraphAreaLocation2(x,y);
		this.setGraphAreaSize2(w,h);
		this.setGraphAreaRectToFigureElement();
		return true;
	}



	/**
	 * 
	 * @return
	 */
	protected boolean setGraphAreaRectToFigureElement()
	{
		Rectangle2D cRect = this.mWnd.getClientRectZoomed();

		// SGIFigureElementにも属性設定
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii]==null )
			{
				continue;
			}
		
			array[ii].setGraphAreaRect(
				this.getGraphAreaX(),
				this.getGraphAreaY(),
				this.getGraphAreaWidth(),
				this.getGraphAreaHeight()
			);
		}
		return true;
	}










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

		mAxisElement.addData( data );
		mGraphElement.addData( data );
		mLegendElement.addData( data );
		mStringElement.addData( data );

		return true;
	}



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




	/**
	 * 
	 */
	protected boolean setViewBounds( final Rectangle2D rectView )
	{
//System.out.println(rectView);

		// 属性に設定
		this.mViewBounds = rectView;

		// SGIFigureElementに設定
		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 ) );
		}

//		this.setViewBounds();

		return true;
	}



	/**
	 * 引数は変化量
	 */
	public boolean resize( final float ratioX, final float ratioY )
	{
//System.out.println("resize: "+ratioX+"  "+ratioY);

		final float x = this.getGraphAreaX()*ratioX;
		final float y = this.getGraphAreaY()*ratioY;
		final float w = this.getGraphAreaWidth()*ratioX;
		final float h = this.getGraphAreaHeight()*ratioY;


		//
		this.setGraphAreaRect(x,y,w,h);

		//
		this.setViewBounds();

		return true;
	}



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

//System.out.println("figure zoom");

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


		// SGIFigureElementにもサイズと倍率を設定する		
		final int nw = this.getWidth();
		final int nh = this.getHeight();
/*		mGraphElement.zoom( 0,0,nw,nh,mag );
		mLegendElement.zoom( 0,0,nw,nh,mag );
		mStringElement.zoom( 0,0,nw,nh,mag );
		mAxisElement.zoom( 0,0,nw,nh,mag );
*/

		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii].zoom( 0,0,nw,nh,mag );
		}

		if( this.setGraphAreaRectToFigureElement() == false )
		{
			return false;
		}


		this.repaint();


		return true;
	}




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























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




	/**
	 * コンポーネントにマウスが入ると呼び出されます。 
	 */
	public void mouseEntered(final MouseEvent e)
	{
	}



	/**
	 * コンポーネントからマウスが出ると呼び出されます。 
	 */
	public void mouseExited(final MouseEvent e)
	{
	}



	/**
	 * コンポーネント上で、ボタンを押さずにマウスを移動すると呼び出されます。 
	 */
	public void mouseMoved(final MouseEvent e)
	{
		final int x = e.getX();
		final int y = e.getY();
//System.out.println(x+"  "+y);

		// 位置情報ラベルへの設定
		this.mWnd.notifyPosition( this, x, y );

		// マウス位置に応じてマウスカーソルを変更
		this.setMouseLocation( x, y );
		this.changeCursor();

		// フィギュア上の描画要素に対する処理
		this.onDrawingElement(x,y);
	}




	/**
	 * コンポーネント上でマウスボタンをクリック (押してから離す) したときに呼び出されます。
	 */
	public void mouseClicked(final MouseEvent e)
	{
		this.onMouseClicked(e);
	}



	/**
	 * 
	 */
	private boolean onMouseClicked(final MouseEvent e)
	{
//System.out.println("Figure : onMouseClicked");

		boolean flag = this.mWnd.onFigureClicked(e);

		if( flag )
		{
			return true;
		}

		return false;
	}


	/**
	 * 
	 * @param e
	 * @return true:有効な場所がクリックされた
	 */
	protected boolean click( MouseEvent e )
	{
//System.out.println("click");

		//
		// トグルボタンが押されていたときの処理
		//

		// 文字列
		final boolean addStringFlag
			= this.mWnd.mAddStringButton.isSelected();
		if( addStringFlag )
		{
System.out.println("addString");
			this.mStringElement.addString( e.getX(), e.getY() );
//			this.mWnd.mAddStringButton.setSelected(false);

			// カーソルをデフォルトの状態に戻す
			this.mWnd.setDefaultCursor();

			// トグルボタンの解除
			this.mWnd.setInsertToggleButtonsSelected(false);

			return true;
		}


		// タイミング描画要素
		final boolean addTimingElementFlag
			= this.mWnd.mAddTimingElementButton.isSelected();
		if( addTimingElementFlag )
		{
System.out.println("addTimingLine");
			this.mAxisElement.addTimingLine( e.getX(), e.getY() );
//			this.mWnd.mAddTimingElementButton.setSelected(false);

			// カーソルをデフォルトの状態に戻す
			this.mWnd.setDefaultCursor();

			// トグルボタンの解除
			this.mWnd.setInsertToggleButtonsSelected(false);

			return true;
		}


		// symbol of siginificant difference
		final boolean addSignificantFlag
			= this.mWnd.mAddSignificantSymbolButton.isSelected();
		if( addSignificantFlag )
		{
System.out.println("addSignificant");
			this.mGraphElement.addSignificantDifferenceSymbol( e.getX(), e.getY() );
//			this.mWnd.mAddSignificantSymbolButton.setSelected(false);

			// カーソルをデフォルトの状態に戻す
			this.mWnd.setDefaultCursor();

			// トグルボタンの解除
			this.mWnd.setInsertToggleButtonsSelected(false);

			return true;
		}



		//
		// SGIFigureElementに問い合わせ
		//

		boolean flag = this.onFigureElementClicked(e);
//System.out.println("I:"+flag);
		if( flag )
		{
			this.afterClicked(e);
			return true;
		}


		// クリックされた点がグラフ描画領域内の場合には、フィギュアがクリックされたとみなす
		if( this.isGraphAreaContains( e.getPoint() ) )
		{

			this.afterClicked(e);

			// マウス右ボタンクリックでポップアップメニューを出す
			if( (SwingUtilities.isRightMouseButton(e) )
				&& ( e.getClickCount() == 1 ) )
			{
				mPopupMenu.show( this, e.getX(), e.getY());
			}

			// マウス左ボタンダブルクリックでダイアログを出す
			if( (SwingUtilities.isLeftMouseButton(e) )
				&& ( e.getClickCount() == 2 ) )
			{
				this.getDialog().show();
			}

			return true;

		}

		return false;

	}


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

		// 自分自身を最前面に出す処理を、所属するウインドウに依頼する
		this.mWnd.mLayeredPane.moveToFront(this);

		this.repaint();


		return true;
	}


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

		boolean flag;

		flag = mStringElement.onMouseClicked(e);
		if( flag )
		{
//System.out.println("String");
			return true;
		}

		flag = mLegendElement.onMouseClicked(e);
		if( flag )
		{
//System.out.println("Legend");
			return true;
		}

		flag = mAxisElement.onMouseClicked(e);
		if( flag )
		{
//System.out.println("Axis");
			return true;
		}

		flag = mGraphElement.onMouseClicked(e);
		if( flag )
		{
//System.out.println("Graph");
			return true;
		}

//System.out.println("Figure");


		return false;
	}




	/**
	 * コンポーネント上でマウスボタンを押すと呼び出されます。 
	 */
	public void mousePressed(final MouseEvent e)
	{
//System.out.println("mousePressed:"+this.getFigureID());

		this.onMousePressed(e);
	}


	/**
	 * 
	 */
	private boolean onMousePressed( final MouseEvent e )
	{
//System.out.println("Figure : onMousePressed");

		boolean flag = this.mWnd.onFigurePressed(e);
		if( flag )
		{
			return true;
		}

		return false;
	}



	/**
	 * 
	 * @param e
	 * @return true:有効な場所がプレスされた
	 */
	protected boolean press( final MouseEvent e )
	{

//System.out.println("press:"+this.getID());

		boolean flag;

		// SGIFigureElementに問い合わせ
		flag = this.onFigureElementPressed(e);
		if( flag )
		{
			this.afterPressed(e);
//System.out.println("mPressedElement:"+this.mPressedElement);
			return true;
		}


		//
		// 有効な点がプレスされたか判定
		//

		flag = false;
		if( this.isGraphAreaContains( e.getPoint() ) )
		{
			flag = true;
		}
		if( this.mMouseLocation != OTHER )
		{
			flag = true;
		}

//System.out.println(flag);
//System.out.println();

		if( flag )
		{
			this.afterPressed(e);
			return true;
		}

		return false;
	}



	/**
	 * 
	 */
	private boolean afterPressed( MouseEvent e )
	{
//System.out.println("<< afterPressed >>");

		//
		// 所属するウインドウの、フォーカスされたフィギュアのリストに対する処理
		//

		final ArrayList fList = this.mWnd.mFocusedFigureList;

		// CTRLキーとSHIFTキーのいずれも押下されていない場合
		if( ( (e.getModifiers()&MouseEvent.CTRL_MASK) == 0 )
			&& ( (e.getModifiers()&MouseEvent.SHIFT_MASK) == 0 ) )
		{
//System.out.println("押下されていない");

			// このフィギュアが既にリストに存在している場合
			if( fList.contains(this) )
			{
				// 何もしない
			}
			else
			{
				this.mWnd.clearFocusedFigures();
				fList.add(this);
			}

		}
		// 押下されている場合
		else
		{
			// このフィギュアが既にリストに存在している場合
			if( fList.contains(this) )
			{
				// 四隅にある場合は除く
				if( this.mMouseLocation!=NORTH_WEST
					&& this.mMouseLocation!=NORTH_EAST
					&& this.mMouseLocation!=SOUTH_EAST
					&& this.mMouseLocation!=SOUTH_WEST )
				{
					fList.remove(this);
				}
			}
			else
			{
				fList.add(this);
			}
		}


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


		SGFigure.mDraggingFlag = true;


		// 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.mLayeredPane.moveToFront(this);


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

//System.out.println("mPressedPoint:"+mPressedPoint);
//System.out.println();

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

//System.out.println();


		// グリッド線とのフィットを調べる
//		this.setFitFlag();


		//
		this.repaint();


		return true;

	}



	/**
	 * 
	 * @return
	 */
	protected boolean updateFigureRect()
	{
		this.mTempFigureRect.setRect( this.getGraphAreaRect() );
		return true;
	}



	/**
	 * ドラッグ中の矩形を設定
	 */
	protected boolean setDraggingRect()
	{
		this.mRectangleOnDragging.setRect(
			this.getGraphAreaX(),
			this.getGraphAreaY(),
			this.getGraphAreaWidth(),
			this.getGraphAreaHeight()
		);

//System.out.println(this.mRectangleOnDragging);

		return true;
	}



	/**
	 * 
	 */
	private boolean onFigureElementPressed( MouseEvent e )
	{
//System.out.println("<< onFigureElementPressed >>");
//System.out.println("id="+this.getID());

		boolean flag;

		flag = mStringElement.onMousePressed(e);
		if( flag )
		{
			mPressedElement = mStringElement;
//System.out.println("String");
			return true;
		}

		flag = mLegendElement.onMousePressed(e);
		if( flag )
		{
			mPressedElement = mLegendElement;
//System.out.println("Legend");
			return true;
		}

		flag = mAxisElement.onMousePressed(e);
		if( flag )
		{
			mPressedElement = mAxisElement;
//System.out.println("Axis");
			return true;
		}

		flag = mGraphElement.onMousePressed(e);
		if( flag )
		{
			mPressedElement = mGraphElement;
//System.out.println("Graph");
			return true;
		}


		return false;
	}


//static int cnt=0;
//static long time = 0;


	/**
	 * コンポーネント上でマウスのボタンを押してドラッグすると呼び出されます。 
	 */
	public void mouseDragged(final MouseEvent e)
	{
//System.out.println("mouseDragged:"+this.getID());
//System.out.println("+++:"+this.mPressedElement);

//long dt = System.currentTimeMillis() - time;
//System.out.println("dt:"+dt);
//time = System.currentTimeMillis();

		this.onMouseDragged(e);

	}



	/**
	 * 
	 */
	private boolean onMouseDragged( final MouseEvent e )
	{
		this.mWnd.onFigureDragged(e);

		return true;
	}




	/**
	 * 
	 */
	protected boolean drag(final MouseEvent e)
	{
//System.out.println();
//System.out.println("<< drag >>");
//System.out.println("id="+this.getID());


		// 位置情報ラベルへの設定
		mWnd.notifyPosition( this, e.getX(), e.getY() );

//System.out.println(e);


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


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



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


		Rectangle2D cRect = this.mWnd.getClientRectZoomed();
		final int diffX = e.getX() - mPressedPoint.x;
		final int diffY = e.getY() - mPressedPoint.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");
			mPressedPoint.setLocation(
				mPressedPoint.getX(),
				mPressedPoint.getY() + diffY
			);
			sizeNewY = sizeOldY - diffY;
			y = yOld + sizeOldY - sizeNewY;
			h = sizeNewY;
		}
		else if( mMouseLocation == SOUTH )
		{
//System.out.println("SOUTH");
			mPressedPoint.setLocation(
				mPressedPoint.getX(),
				mPressedPoint.getY() + diffY
			);
			sizeNewY = sizeOldY + diffY;
			h = sizeNewY;
		}
		else if( mMouseLocation == WEST )
		{
//System.out.println("WEST");
			mPressedPoint.setLocation(
				mPressedPoint.getX() + diffX,
				mPressedPoint.getY() + diffY
			);
			sizeNewX = sizeOldX - diffX;
			x = xOld + sizeOldX - sizeNewX;
			w = sizeNewX;
		}
		else if( mMouseLocation == EAST )
		{
//System.out.println("EAST");
			mPressedPoint.setLocation(
				mPressedPoint.getX() + diffX,
				mPressedPoint.getY()
			);
			sizeNewX = sizeOldX + diffX;
			w = sizeNewX;
		}
		else if( mMouseLocation == NORTH_WEST )
		{
			mPressedPoint.setLocation(
				mPressedPoint.getX() + diffX,
				mPressedPoint.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 )
		)
		{
			mPressedPoint.setLocation(
				mPressedPoint.getX() + diffX,
				mPressedPoint.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 )
		)
		{
			mPressedPoint.setLocation(
				mPressedPoint.getX(),
				mPressedPoint.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 )
		)
		{

			mPressedPoint.setLocation(
				mPressedPoint.getX() + diffX,
				mPressedPoint.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 )
		)
		{
			mPressedPoint.setLocation(
				mPressedPoint.getX() + diffX,
				mPressedPoint.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 )
		)
		{
			mPressedPoint.setLocation(
				mPressedPoint.getX() + diffX,
				mPressedPoint.getY() + diffY
			);

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

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

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

			w = sizeNewX;
			h = sizeNewY;
		}


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


		// 矩形を更新
		this.mRectangleOnDragging.setRect( x, y, w, h );
//System.out.println(this.mRectangleOnDragging);


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


		// repaint
		this.repaint();

//System.out.println();

		return true;

	}



	/**
	 * 
	 */
	private boolean dragOtherPoint( final MouseEvent e )
	{
//System.out.println("<< dragOtherPoint >>");
//System.out.println("id="+this.getID());

//cnt++;
//System.out.println(this.mPressedElement);


		// クリックされたSGIFigureElementがあれば問い合わせ
		if( mPressedElement!=null )
		{
			// フィギュアエレメントに知らせる
			final boolean flag = mPressedElement.onMouseDragged(e);
//System.out.println(flag);
//System.out.println();
			return flag;
		}


		// parallel displacement
		if( this.isGraphAreaContains( mPressedPoint ) )
		{
//System.out.println("parallel");
//System.out.println();

			final int dx = e.getX() - mPressedPoint.x;
			final int dy = e.getY() - mPressedPoint.y;

//System.out.println(e.getPoint());
//System.out.println(this.mPressedPoint);
//System.out.println(dx*SGConstants.CM_POINT_RATIO+"  "+dy*SGConstants.CM_POINT_RATIO);

			// 選択されている全てのフィギュアを平行移動
			ArrayList list = this.mWnd.mFocusedFigureList;
//System.out.println("list:"+list);
			for( int ii=0; ii<list.size(); ii++ )
			{
//System.out.println("ii="+ii);

				final SGFigure figure = (SGFigure)list.get(ii);

//System.out.println("id="+figure.getID());

//System.out.println("before:"+figure.getRectangleOnDragging());

				// ドラッグ中の矩形に設定
				figure.getRectangleOnDragging().setRect(
					figure.getGraphAreaX() + dx,
					figure.getGraphAreaY() + dy,
					figure.getGraphAreaWidth(),
					figure.getGraphAreaHeight()
				);

				if( SGFigure.mDrawRectangleOnDraggingFlag == false )
				{
					figure.mPressedPoint.setLocation(
						figure.mPressedPoint.x + dx,
						figure.mPressedPoint.y + dy
					);

					figure.setGraphAreaRectOnDragging();

				}

//System.out.println("after:"+figure.getRectangleOnDragging());

			}


			this.repaint();
		}

//System.out.println();

		return true;

	}



	/**
	 * 
	 */
	private boolean setGraphAreaRectOnDragging()
	{
		Rectangle2D rect = this.getRectangleOnDragging();

		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.setGraphAreaRect(x,y,w,h);

		return true;
	}



	/**
	 * コンポーネント上でマウスボタンを離すと呼び出されます。 
	 */
	public void mouseReleased(final MouseEvent e)
	{
		this.onMouseReleased(e);
	}



	/**
	 * 
	 * @param e
	 * @return
	 */
	private boolean onMouseReleased( final MouseEvent e )
	{

		this.mWnd.onFigureReleased(e);

		return true;
	}



	/**
	 * 
	 * @return
	 */
	protected boolean release( final MouseEvent e )
	{
//System.out.println("release:"+this.getID());
//System.out.println("mPressedElement="+this.mPressedElement);
//System.out.println();


		// カーソルをデフォルトの状態に戻す
		this.mWnd.setDefaultCursor();


		// ドラッグ中の矩形を元に、フィギュアの境界を設定
		// mPressedElementが存在する場合には、SGIFigureElementの
		// 更新が優先されるので、以下の処理は行わない
		if( SGFigure.mDrawRectangleOnDraggingFlag
			&& this.mPressedElement==null )
		{
			this.setGraphAreaRectOnDragging();
		}


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


		SGFigure.mDraggingFlag = false;


		// マウスのプレス時とリリース時とでフィギュアの位置が変わらない場合には、
		// これ以降の処理は不要なので、このまま終了
//		if( this.mTempFigureRect == null )
//		{
//			return true;
//		}
		if( this.isFigureMoved() == false )
		{
//System.out.println(this.getLocation()+"  "+this.mFigureLocation);
			this.repaint();
			return true;
		}



		// フィギュアがクライアント領域の外にはみ出た場合には引き戻す
		for( int ii=0; ii<this.mWnd.mFocusedFigureList.size(); ii++ )
		{
//System.out.println("ii="+ii);
			SGFigure figure = (SGFigure)this.mWnd.mFocusedFigureList.get(ii);
			figure.drawbackFigure();
		}
//System.out.println();


		// After drawing back legend and string elements,
		// check whether the bounds line shoukd be written.
		this.mStringElement.chkBoundsVisible();
		this.mLegendElement.chkBoundsVisible();


		// 履歴を残す
/*		boolean flag = this.updateHistory();
		if( !flag )
		{
			return false;
		}
*/

		//
//		this.setFitFlag();


		//
		this.repaint();


		return true;
	}




	/**
	 * 
	 */
	protected boolean isFigureMoved()
	{
//System.out.println("###");
//System.out.println(this.mTempFigureRect);
//System.out.println(this.mGraphAreaX+"  "+this.mGraphAreaY+"  "+this.mGraphAreaWidth+"  "+this.mGraphAreaHeight);
//System.out.println();

		final boolean flag = !( this.mTempFigureRect.equals( this.getGraphAreaRect() ) );


/*
		final boolean flag = !(
			(this.mGraphAreaX==this.mTempFigureRect.getX())
			&&
			(this.mGraphAreaY==this.mTempFigureRect.getY())
			&&
			(this.mGraphAreaWidth==this.mTempFigureRect.getWidth())
			&&
			(this.mGraphAreaHeight==this.mTempFigureRect.getHeight())
		 );
*/
		
		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.setDraggingRect();

//System.out.println(this.mRectangleOnDragging);
//System.out.println(this.getGraphAreaRect());
//System.out.println();

		return true;
	}



	/**
	 * 
	 * @return
	 */
	public boolean clearSelectedElements()
	{
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii].clearSelectedElements();
		}
		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){}



	/**
	 * 
	 */
	protected boolean onKeyPressed( final KeyEvent e )
	{
		
		this.mAxisElement.onKeyPressed(e);
		this.mLegendElement.onKeyPressed(e);
		this.mGraphElement.onKeyPressed(e);
		this.mStringElement.onKeyPressed(e);
		
		return true;
	}


	/**
	 * 
	 */
	protected boolean onKeyReleased( final KeyEvent e )
	{

		this.mAxisElement.onKeyReleased(e);
		this.mLegendElement.onKeyReleased(e);
		this.mGraphElement.onKeyReleased(e);
		this.mStringElement.onKeyReleased(e);

		return true;
	}


	/**
	 * 
	 */
	protected boolean onKeyTyped( final KeyEvent e )
	{

//		this.typed(e);

		this.mAxisElement.onKeyTyped(e);
		this.mLegendElement.onKeyTyped(e);
		this.mGraphElement.onKeyTyped(e);
		this.mStringElement.onKeyTyped(e);

		return true;
	}



	/**
	 * 
	 */
	private boolean typed( final KeyEvent e )
	{

/*
		if( this.isFocusedFigure() )
		{

			final char c = e.getKeyChar();
			final int code = e.getKeyCode();


//System.out.println("+++");
//System.out.println(c);
//System.out.println(code);
//System.out.println("***");
//System.out.println();


			switch( c )
			{

				case 'k' :
				{
					this.translate( 0.0, -1.0 );
					break;
				}

				case 'j' :
				{
					this.translate( 0.0, 1.0 );
					break;
				}

				case 'h' :
				{
					this.translate( -1.0, 0.0 );
					break;
				}

				case 'l' :
				{
					this.translate( 1.0, 0.0 );
					break;
				}

				default :
				{
					
				}
			}

		}

		this.repaint();
*/

		return true;
	}




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

		// フィギュアが属するウインドウ
		if( obj.equals( this.mWnd ) )
		{
System.out.println("windowActivated");

			if( this.mDialog.isShowing() )
			{
//				this.mDialog.toFront();
			}

		}
*/
	}


	/**
	 * 
	 */
	public void windowDeactivated(final WindowEvent e)
	{
//System.out.println("windowDeactivated");
	}


	/**
	 * 
	 */
	public void windowIconified(final WindowEvent e)
	{
//System.out.println("windowIconified");
	}


	/**
	 * 
	 */
	public void windowDeiconified(final WindowEvent e)
	{
//System.out.println("windowDeiconified");
	}


	/**
	 * 
	 */
	public void windowOpened(final WindowEvent e)
	{
//System.out.println("windowOpened");
	}


	/**
	 * 
	 */
	public void windowClosed(final WindowEvent e)
	{
//System.out.println("windowClosed");
	}


	/**
	 * 
	 */
	public void windowClosing(final WindowEvent e)
	{
//System.out.println("windowClosing");

		Object obj = e.getSource();


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

	}



	/**
	 * 
	 * @return
	 */
	public boolean undo()
	{
//System.out.println("undo "+this);

		this.mFigureStateCounter--;

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

//System.out.println(this.mFigureStateCounter);
//System.out.println();

		this.setGraphAreaRectToFigureElement();


//		this.calcElementBounds();
//		this.setViewBounds();
		this.repaint();

		return true;
	}



	/**
	 * 
	 * @return
	 */
	public boolean redo()
	{
//System.out.println("redo "+this);

	this.mFigureStateCounter++;

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

//System.out.println(this.mFigureStateCounter);
//System.out.println();

		this.setGraphAreaRectToFigureElement();

//		this.calcElementBounds();
//		this.setViewBounds();
		this.repaint();

		return true;

	}



	/**
	 * 
	 */
	protected Rectangle2D getRectangleOnDragging()
	{
		return this.mRectangleOnDragging;
	}



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

		final double radius = 1.5*this.mWnd.mAnchorSize;

		final Rectangle2D rect = this.getGraphAreaRect();


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

//System.out.println(mMouseLocation);
	}



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


		// カーソルを、属するウインドウにセット
		// フィギュアにセットすると表示されない（AnchorPanelのため）
		if( mWnd.mFocusedFigureList.contains(this) )
		{
			this.mWnd.setCursor( cur );
		}


		return true;
	}




	/**
	 * 
	 */
	private boolean onDrawingElement( final int x, final int y )
	{
		boolean flag;

		flag = mStringElement.onDrawingElement(x,y);
		if( flag )
		{
//System.out.println("String");
			return true;
		}

		flag = mLegendElement.onDrawingElement(x,y);
		if( flag )
		{
//System.out.println("Legend");
			return true;
		}

		flag = mAxisElement.onDrawingElement(x,y);
		if( flag )
		{
//System.out.println("Axis");
			return true;
		}

		flag = mGraphElement.onDrawingElement(x,y);
		if( flag )
		{
//System.out.println("Graph");

			Cursor cur = this.mGraphElement.getFigureElementCursor();
			if( cur!=null )
			{
				this.mWnd.setCursor(cur);
				repaint();
			}

			return true;
		}

		return false;
	}



	/**
	 * 
	 */
	protected final String DRAW_DRAGGING_RECTANGLE = "Draw Dragging Rectangle";


	/**
	 * 
	 */
	protected final String SAVE_PROPERTY = "Save the Property";


	/**
	 * 
	 */
	private boolean createPopupMenu()
	{
		mPopupMenu.setBounds( 0, 0, 100, 100 );


		final String[] commandArray = {
			DRAW_DRAGGING_RECTANGLE,
			SAVE_PROPERTY,
			REMOVE_FIGURE
		};


		final String[] commandArray2 = {
			SHOW_FRAME
		};


		for( int ii=0; ii<commandArray.length; ii++ )
		{
			final JMenuItem item = new JMenuItem(commandArray[ii]);
			item.addActionListener(this);
			this.mPopupMenu.add(item);
		}

		this.mPopupMenu.addSeparator();

		for( int ii=0; ii<commandArray2.length; ii++ )
		{
			final JMenuItem item = new JMenuItem(commandArray2[ii]);
			item.addActionListener(this);
			this.mPopupMenu.add(item);
		}


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

System.out.println("setDialogProperty");

		final SGFigureDialog dg = (SGFigureDialog)this.mDialog;

System.out.println(this.mGraphAreaX);
System.out.println(this.mGraphAreaX*SGConstants.CM_POINT_RATIO+" cm");

		// SGFigure
		dg.setFigureX( this.mGraphAreaX*SGConstants.CM_POINT_RATIO );
		dg.setFigureY( this.mGraphAreaY*SGConstants.CM_POINT_RATIO );
		dg.setFigureWidth( this.mGraphAreaWidth*SGConstants.CM_POINT_RATIO );
		dg.setFigureHeight( this.mGraphAreaHeight*SGConstants.CM_POINT_RATIO );

		dg.setFigureBackgroundColor( this.mBackgroundColor );

		dg.setFigureTransparentFlag( this.mTransparentFlag );

/*
System.out.println("mX="+mX+",  "+this.mX*SGConstants.CM_POINT_RATIO);
System.out.println("mY="+mY+",  "+this.mY*SGConstants.CM_POINT_RATIO);
System.out.println("mWidth="+mWidth+",  "+this.mWidth*SGConstants.CM_POINT_RATIO);
System.out.println("mHeight="+mHeight+",  "+this.mHeight*SGConstants.CM_POINT_RATIO);
System.out.println();
*/

		dg.setFigureSpaceLineAndNumber( this.mSpaceAxisLineAndNumber*SGConstants.CM_POINT_RATIO );
		dg.setFigureSpaceNumberAndTitle( this.mSpaceNumberAndTitle*SGConstants.CM_POINT_RATIO );



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



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

//		this.calcElementBounds();
//		this.setViewBounds();
		this.repaint();

		return true;

	}




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

		// location
		p.x = this.mGraphAreaX;
		p.y = this.mGraphAreaY;

		// size
		p.width = this.mGraphAreaWidth;
		p.height = this.mGraphAreaHeight;

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

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

/*
		final FigureProperties p = new FigureProperties();
		final SGFigureDialog dg = (SGFigureDialog)this.mDialog;

		// x
		final float x = dg.getFigureX()/SGConstants.CM_POINT_RATIO;

		// y
		final float y = dg.getFigureY()/SGConstants.CM_POINT_RATIO;

		// width
		final float w = dg.getFigureWidth()/SGConstants.CM_POINT_RATIO;

		// height
		final float h = dg.getFigureHeight()/SGConstants.CM_POINT_RATIO;

		p.x = x;
		p.y = y;
		p.width = w;
		p.height = h;


		// space
		p.spaceLineAndNumber = dg.getSpaceLineAndNumber()/SGConstants.CM_POINT_RATIO;
		p.spaceNumberAndTitle = dg.getSpaceNumberAndTitle()/SGConstants.CM_POINT_RATIO;


		// legend
		p.legendVisible = dg.getFigureLegendVisible();


		// background color
		p.bgColor = dg.getFigureBackgroundColor();


		// transparency
		p.transparent = dg.getFigureTransparentFlag();
*/

		return p;
	}



	/**
	 * 
	 */
	private boolean setBackgroundColorWidhDialog()
	{
		SGFigureDialog dg = (SGFigureDialog)this.mDialog;
		this.mBackgroundColor = dg.getFigureBackgroundColor();
		
		return true;
	}



	/**
	 * アクションが発生すると呼び出されます。
	 */
	public void actionPerformed(final ActionEvent e)
	{

//System.out.println("Figure : actionPerformed");
//System.out.println(e);
//System.out.println();


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


		//
		// popup menu
		//

		if( strCommand.equals(SHOW_FRAME) )
		{
			mBoundingBoxVisibleFlag = !mBoundingBoxVisibleFlag;
		}
		else if( strCommand.equals(DRAW_DRAGGING_RECTANGLE) )
		{
			SGFigure.mDrawRectangleOnDraggingFlag = !SGFigure.mDrawRectangleOnDraggingFlag;
		}
		else if( strCommand.equals(REMOVE_FIGURE) )
		{
			this.mWnd.hideSelectedFigures();
		}
		else if( strCommand.equals(SAVE_PROPERTY) )
		{
			this.mWnd.createPropertyFileForFocusedFigures();
		}


		//
		// dialog
		//

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

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


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

		if( source instanceof SGIFigureElement )
		{
			this.fromFigureElement(e);
		}


		this.repaint();


		return;

	}


	/**
	 * データの同期を取る
	 */
	private boolean mergeDatas( final SGIFigureElement element )
	{
//System.out.println("mergeDatas");

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

//System.out.println(this.mDataList);
//System.out.println(differenceDataList);
//System.out.println();

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

		return true;

	}



	/**
	 * 
	 */
	private boolean fromFigureElement( final ActionEvent e )
	{

//System.out.println("+++ fromFigureElement +++");
//System.out.println(e);
//System.out.println();

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

		final int id = e.getID();

		switch( id )
		{

			// SGIFigureElementの変更を伝える場合
			case SGFigure.NOTIFY_CHANGE :
			{

				// データの同期を取る
				this.mergeDatas( element );


				// broadcastして同期を取る
				this.notifyFigureElement( element );


				break;

			}


			// 履歴を更新する場合
			case SGFigure.UPDATE_HISTORY :
			{
				// Figureの履歴を更新
				this.updateHistory( element );

				break;
			}



			// 共通プロパテイ設定
			case SGFigure.SET_PROPERTY_OF_SELECTED_DATA :
			{
				//
				this.mWnd.setPropertyOfSelectedObjects();

				break;
			}


			default :
			{
				
			}

		}


		return true;

	}



	/**
	 * 
	 * @return
	 */
	public boolean notifyFigureElement( SGIFigureElement element )
	{
		this.broadcast(element);
//		this.calcElementBounds();

		return true;
	}



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



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



	/**
	 * 
	 */
	public boolean addPropertiesHistory()
	{
		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()
	{
System.out.println("onOK");

		boolean flag;

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


		// ダイアログを出す前後でプロパティが変更されている場合のみ、
		// 履歴を更新する
		SGProperties pTemp = this.mTemporaryProperties;
		SGProperties pPresent = this.getProperties();
System.out.println("pTemp:"+pTemp);
System.out.println("pPresent:"+pPresent);
System.out.println(pTemp.equals(pPresent));
System.out.println();
		if( pTemp.equals(pPresent) == false )
		{
			// ウインドウのオブジェクト履歴を更新
			this.updateParentHistory();

			// オブジェクト履歴を更新
			this.updateHistory();
		}


		this.mTemporaryProperties = null;
		this.mDialog.hide();
		this.repaint();


		return true;

	}



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

//System.out.println("SGFigure : updateHistory()");
//System.out.println();


		// オブジェクト履歴を更新
		this.updateObjectHistory(this);


		// フィギュアのプロパティ履歴を更新
		this.mFigureStateCounter++;
		this.addFigurePropertyHistory( (FigureProperties)this.getProperties() );


		return true;

	}



	/**
	 * 
	 */
	public boolean updateParentHistory()
	{
		this.mWnd.updateObjectHistory(this);
		return true;
	}



	/**
	 * 
	 */
	protected boolean updateHistory( SGIFigureElement el )
	{
//System.out.println("updateHistory");
//System.out.println(el);
//System.out.println();

		// ウインドウのオブジェクト履歴を更新
		this.updateParentHistory();

		// オブジェクト履歴を更新
		this.updateObjectHistory(el);


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

		this.mTemporaryProperties = null;
		this.mDialog.hide();
		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.mGraphAreaX = leftMax;
		this.mGraphAreaY = topMax;


		// 属性の全てのSGIFigureElementに値を設定し、
		// 描画要素を作り直させる
		final SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii].setGraphAreaLocation( this.mGraphAreaX, this.mGraphAreaY );
			array[ii].setGraphAreaSize( this.mGraphAreaWidth, this.mGraphAreaHeight );
		}

		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].getMarginAroundGraphAreaRect(
				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.getGraphAreaRect();

		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.setGraphAreaRect( 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 setGraphAreaLocationByLeftBottom( final float x, final float y )
	{
		Rectangle2D gRect = this.getGraphAreaRect();
		this.setGraphAreaLocation( 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 )
	{
//System.out.println("*** "+this+" ***");	

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

//System.out.println(this.mDataList);

				for( int jj=0; jj<array.length; jj++ )
				{
					array[jj].removeData(data);
//System.out.println(jj+"   "+array[jj].getDataList());
				}

//System.out.println();

				return true;
			}
		}

		return true;
	}



	/**
	 * 描画メソッド
	 */
	public void paintComponent(final Graphics g)
	{
		super.paintComponent(g);
		final Graphics2D g2d = (Graphics2D) g;
		this.paintGraphics2D(g2d);
	}



	/**
	 * 
	 * @param g2d
	 */
	public void paintGraphics2D( final Graphics2D g2d )
	{

		final Rectangle2D graphAreaRect = this.getGraphAreaRect();
		if( this.mTransparentFlag == false )
		{
			g2d.setPaint(this.mBackgroundColor);
			g2d.fill(graphAreaRect);
		}


if( this.mBoundingBoxVisibleFlag )
{
	Rectangle2D rect = this.getBoundingBox();
	if( rect!=null )
	{
		g2d.setPaint( Color.BLUE );
		g2d.setStroke( new BasicStroke(3) );
		g2d.draw( rect );
	}
}


	}



	/**
	 * フレームの線を作成する
	 */
	private void drawFrameLines( final Graphics2D g2d )
	{

		g2d.setPaint( Color.RED );
		g2d.setStroke( new BasicStroke(5) );
		g2d.draw( new Rectangle( 0, 0, this.getWidth(), this.getHeight() ) );

	}



	/**
	 * 
	 * @return
	 * @throws IOException
	 */
	public boolean createPropertyFileAll( final Writer writer ) throws IOException
	{

		// フィギュアのプロパティ
		this.writeProperty(writer);


		// SGIFigureElementのプロパティ
		// SGIFigureElementのプロパティ
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii].writeProperty(writer);
		}


		// データのプロパティ
		this.mGraphElement.writePropertyOfData( writer );

		writer.write("\n");

		return true;
	}



	/**
	 * 
	 * @return
	 * @throws IOException
	 */
	public boolean createPropertyFileAllOnFocused( final Writer writer ) throws IOException
	{
//System.out.println("createPropertyFileAllOnFocused");

		// フィギュアのプロパティ
		this.writePropertyOnFocused(writer);


		// SGIFigureElementのプロパティ
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii].writeProperty(writer);
		}


		// データのプロパティ
		this.mGraphElement.writePropertyOfData( writer );

		writer.write("\n");

		return true;
	}




	/**
	 * 
	 * @return
	 * @throws IOException
	 */
	public boolean createPropertyFile( final Writer writer ) throws IOException
	{

		// フィギュアのプロパティ
		this.writeProperty(writer);

		return true;

	}



	public static final String PF_FIGURE_TITLE = "[Figure]";
	public static final String PF_CLASS_NAME = "Class Name";
	public static final String PF_FIGURE_ID = "ID";
	public static final String PF_FIGURE_X_IN_CLIENT = "X";
	public static final String PF_FIGURE_Y_IN_CLIENT = "Y";
	public static final String PF_WIDTH = "Width";
	public static final String PF_HEIGHT = "Height";
	public static final String PF_SPACE_AXIS_LINE_AND_NUMBER = "Space between Axis Line and Numbers";
	public static final String PF_SPACE_NUMBER_AND_TITLE = "Space between Numbers and Title";
	public static final String PF_BACKGROUND_COLOR = "Background Color";
	public static final String PF_TRANSPARENCY = "Transparency";
	public static final String PF_LEGEND_VISIBLE = "Legend Visible";


	/**
	 * 
	 */
	public boolean writeProperty( final Writer writer ) throws IOException
	{
		writer.write( PF_FIGURE_TITLE + "\n" );
		final float x = this.mGraphAreaX;
		final float y = this.mGraphAreaY;
		this.writeProperty_( writer, x, y );
		writer.write("\n\n");

		return true;
	}


	/**
	 * 
	 */
	public boolean writePropertyOnFocused( final Writer writer ) throws IOException
	{

/*
		writer.write( PF_FIGURE_TITLE + "\n" );

		final Rectangle2D bb = this.mWnd.getBoundingBoxOfFigures( this.mWnd.mFocusedFigureList );
		final float x
			= (float)( this.getX() - bb.getX() )/this.mMagnification
				+ this.mGraphAreaX;
		final float y
			= (float)( this.getY() - bb.getY() )/this.mMagnification
				+ this.mGraphAreaY;


//System.out.println(bb);
//System.out.println(this.getX()+"  "+this.getY());
//System.out.println(this.mGraphAreaX+"  "+this.mGraphAreaY);
//System.out.println();


		this.writeProperty_( writer, x, y );


		writer.write("\n");
*/
		return true;
	}



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



	/**
	 * 
	 * @return
	 * @throws IOException
	 */
	private boolean writeProperty_( final Writer writer, final float x, final float y )
		throws IOException
	{

		SGUtilityText.writePropertyLine( writer, PF_CLASS_NAME, this.getClassType() );
//		SGUtilityForPropertyFile.writePropertyLine( writer, PF_CLASS_NAME, this.getClass().getName() );
		SGUtilityText.writePropertyLine( writer, PF_FIGURE_X_IN_CLIENT, new Float( x*SGConstants.CM_POINT_RATIO ) );
		SGUtilityText.writePropertyLine( writer, PF_FIGURE_Y_IN_CLIENT, new Float( y*SGConstants.CM_POINT_RATIO ) );
		SGUtilityText.writePropertyLine( writer, PF_WIDTH, new Float( this.mGraphAreaWidth*SGConstants.CM_POINT_RATIO ) );
		SGUtilityText.writePropertyLine( writer, PF_HEIGHT, new Float( this.mGraphAreaHeight*SGConstants.CM_POINT_RATIO ) );
		SGUtilityText.writePropertyLine( writer, PF_SPACE_AXIS_LINE_AND_NUMBER, new Float( this.mSpaceAxisLineAndNumber*SGConstants.CM_POINT_RATIO ) );
		SGUtilityText.writePropertyLine( writer, PF_SPACE_NUMBER_AND_TITLE, new Float( this.mSpaceNumberAndTitle*SGConstants.CM_POINT_RATIO ) );
		SGUtilityText.writeColorPropertyLine( writer, PF_BACKGROUND_COLOR, this.mBackgroundColor );
		SGUtilityText.writePropertyLine( writer, PF_TRANSPARENCY, new Boolean( this.mTransparentFlag ) );
		SGUtilityText.writePropertyLine( writer, PF_LEGEND_VISIBLE, new Boolean( this.mLegendElement.isLegendVisible() ) );

		return true;
	}



	/**
	 * 
	 */
	private boolean removeAllElements()
	{
		this.mDataList.clear();

		this.mAxisElement.removeAllElements();
		this.mGraphElement.removeAllElements();
		this.mLegendElement.removeAllElements();
		this.mStringElement.removeAllElements();

		return true;
	}




	/**
	 * 
	 */
	private FigureProperties readProperties(
		final BufferedReader br )
		throws IOException, ClassNotFoundException,
			InstantiationException, IllegalAccessException
	{

//System.out.println("readProperty");

		FigureProperties p = new FigureProperties();


		String line = null;
		float unit = SGConstants.CM_POINT_RATIO;


		// title
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		if( line.equals( PF_FIGURE_TITLE ) == false )
		{
			return null;
		}


		// class name
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}



		//
		// bounds of the figure
		//

		// x
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Double dX = SGUtilityText.getDoublePropertyFromLine( line, SGFigure.PF_FIGURE_X_IN_CLIENT );
		if( dX == null )
		{
			return null;
		}
		float x = dX.floatValue()/unit;
//System.out.println(x);


		// y
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Double dY = SGUtilityText.getDoublePropertyFromLine( line, SGFigure.PF_FIGURE_Y_IN_CLIENT );
		if( dY == null )
		{
			return null;
		}
		float y = dY.floatValue()/unit;
//System.out.println(y);


		// width
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Double dW = SGUtilityText.getDoublePropertyFromLine( line, SGFigure.PF_WIDTH );
		if( dW == null )
		{
			return null;
		}
		float w = dW.floatValue()/unit;
//System.out.println(w);


		// height
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Double dH = SGUtilityText.getDoublePropertyFromLine( line, SGFigure.PF_HEIGHT );
		if( dH == null )
		{
			return null;
		}
		float h = dH.floatValue()/unit;
//System.out.println(h);


		p.x = x;
		p.y = y;
		p.width = w;
		p.height = h;



		//
		// space
		//

		// line and numbers
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Double dSpaceLN = SGUtilityText.getDoublePropertyFromLine( line, SGFigure.PF_SPACE_AXIS_LINE_AND_NUMBER );
		if( dSpaceLN == null )
		{
			return null;
		}
		float spaceLN = dSpaceLN.floatValue()/unit;
//System.out.println(spaceLN);


		// numbers and title
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Double dSpaceNT = SGUtilityText.getDoublePropertyFromLine( line, SGFigure.PF_SPACE_NUMBER_AND_TITLE );
		if( dSpaceNT == null )
		{
			return null;
		}
		float spaceNT = dSpaceNT.floatValue()/unit;
//System.out.println(spaceNT);


		p.spaceLineAndNumber = spaceLN;
		p.spaceNumberAndTitle = spaceNT;



		//
		// BackgroundColor
		//
		
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Color bgColor = SGUtilityText.getColorPropertyFromLine( line, SGFigure.PF_BACKGROUND_COLOR );
		if( bgColor == null )
		{
			return null;
		}

		p.bgColor = bgColor;
//System.out.println(bgColor);



		//
		// Transparency
		//
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Boolean bTransparency = SGUtilityText.getBooleanPropertyFromLine( line, SGFigure.PF_TRANSPARENCY );
		if( bTransparency == null )
		{
			return null;
		}
		boolean transparency = bTransparency.booleanValue();

		p.transparent = transparency;



		//
		// Legend Visible
		//
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Boolean bLegendVisible = SGUtilityText.getBooleanPropertyFromLine( line, SGFigure.PF_LEGEND_VISIBLE );
		if( bLegendVisible == null )
		{
			return null;
		}
		boolean legendVisible = bLegendVisible.booleanValue();

		p.legendVisible = legendVisible;
//System.out.println("read:"+legendVisible);


		return p;

	}



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

		FigureProperties fp = (FigureProperties)p;

		this.mGraphAreaX = fp.x;
		this.mGraphAreaY = fp.y;
		this.mGraphAreaWidth = fp.width;
		this.mGraphAreaHeight = fp.height;

//		this.setGraphAreaLocation(fp.x,fp.y);
//		this.setGraphAreaSize(fp.width,fp.height);
		this.setSpaceAxisLineAndNumber(fp.spaceLineAndNumber);
		this.setSpaceNumberAndTitle(fp.spaceNumberAndTitle);
		this.setBackgroundColor(fp.bgColor);
		this.setTransparent(fp.transparent);
		this.setLegendVisible(fp.legendVisible);

		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;

		}


	}



	/**
	 *
	 */
	public boolean createDataObjectFromPropertyFile(
		final BufferedReader br, final SGData data )
		throws IOException, ClassNotFoundException,
			InstantiationException, IllegalAccessException
	{

//System.out.println();
//System.out.println("<< createDataObjectFromPropertyFile >>");
//System.out.println(data);
//System.out.println();

		// データをリストに追加
		this.mDataList.add( data );


		// SGIFigureElementにデータとBufferedReaderを渡して
		// データに対応するオブジェクトを作成する
		boolean fFlag;
		fFlag = this.mGraphElement.createDataObject( br, data );
		if( !fFlag )
		{
			return false;
		}
//System.out.println("graph");

		fFlag = this.mLegendElement.createDataObject( br, data );
		if( !fFlag )
		{
			return false;
		}
//System.out.println("legend");

		fFlag = this.mStringElement.createDataObject( br, data );
		if( !fFlag )
		{
			return false;
		}
//System.out.println("string");

		fFlag = this.mAxisElement.createDataObject( br, data );
		if( !fFlag )
		{
			return false;
		}
//System.out.println("axis");

		return true;

	}



}
