
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 ActionListener, ComponentListener, WindowListener,
		SGIConstants, 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 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;


	/**
	 * 一時的に保存しておくデータのリスト
	 */
	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 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 int NOTIFY_CHANGE = 0;


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


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


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


	/**
	 * 
	 */
	public static final int FOCUSED_OBJECTS_CHANGED = 4;


	/**
	 * 
	 */
	public static final int NOTIFY_CHANGE_TO_ROOT = 5;


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


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


	/**
	 * 
	 */
	private boolean mCompletedFlag = false;


	/**
	 * 
	 * @return
	 */
	public boolean isCompleted()
	{
		return this.mCompletedFlag;
	}


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

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

		return true;
	}



	/**
	 * 
	 */
	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.mMagnification;
		this.mAxisElement.setSpaceAxisLineAndNumber( space );
		return true;
	}


	/**
	 * 
	 * @return
	 */
	public boolean setSpaceNumberAndTitle( final float space )
	{
		this.mSpaceNumberAndTitle = space/this.mMagnification;
		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;
	}



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

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

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



	/**
	 * 
	 * @return
	 */
	public float getGraphAreaY()
	{
		final Rectangle2D cRect = this.mWnd.mClientRect;
		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.mClientRect;
		this.mGraphAreaX = ( x - (float)cRect.getX() )/this.mMagnification;
		this.mGraphAreaY = ( y - (float)cRect.getY() )/this.mMagnification;

//this.setGraphAreaLocationRoundingOut(x,y);
		return true;
	}



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

		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.mGraphAreaX = x;
		this.mGraphAreaY = y;

		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;
//this.setGraphAreaSizeRoundingOut( w, h );
		return true;
	}



	/**
	 * 
	 * @param widthPt
	 * @param heightPt
	 * @return
	 */
	protected boolean setGraphAreaSizeRoundingOut(
		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.mGraphAreaWidth = width;
		this.mGraphAreaHeight = height;

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

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

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


		//
		this.mWnd.updateDataItem();

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

		this.mGraphAreaX *= ratioX;
		this.mGraphAreaY *= ratioY;
		this.mGraphAreaWidth *= ratioX;
		this.mGraphAreaHeight *= ratioY;

		//
		this.setGraphAreaRectToFigureElement();

		//
		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に倍率を設定する		
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii].zoom( mag );
		}
		if( this.setGraphAreaRectToFigureElement() == false )
		{
			return false;
		}


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

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










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



	/**
	 * 
	 * @param e
	 * @return
	 */
	protected boolean onMouseMoved( final MouseEvent e )
	{
//System.out.println("ID="+this.getID());

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


		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
			this.setMouseCursor( x, y );
			return true;
		}

		return false;
	}



	/**
	 * 
	 * @param e
	 * @return
	 */
	protected boolean onToggleSelected( MouseEvent e )
	{

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


		// a label
		if( this.mWnd.mInsertLabelButton.isSelected() )
		{
			final boolean flag = this.mStringElement.addString(x,y);
			if( flag )
			{
				// カーソルをデフォルトの状態に戻す
				this.mWnd.setDefaultCursor();

				// set all items unselected for insert
				this.mWnd.setInsertToggleItemsUnselected();

				return true;
			}
			else
			{
				SGUtility.showMessageDialog(
					this.mWnd,
					"Click the figure.",
					"Failed to add a label.", 
					JOptionPane.WARNING_MESSAGE );

				return false;
			}

		}


		// a timing line
		if( this.mWnd.mInsertTimingLineButton.isSelected() )
		{
			final boolean flag = this.mAxisElement.addTimingLine(x,y);
			if( flag )
			{
				// カーソルをデフォルトの状態に戻す
				this.mWnd.setDefaultCursor();

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

				return true;
			}
			else
			{
				SGUtility.showMessageDialog(
					this.mWnd,
					"Click the axis line.",
					"Failed to add a timing line.", 
					JOptionPane.WARNING_MESSAGE );

				return false;
			}

		}



		// an axis break symbol
		if( this.mWnd.mInsertBreakButton.isSelected() )
		{
			final boolean flag = this.mAxisElement.addAxisBreakSymbol(x,y);
			if( flag )
			{
				// カーソルをデフォルトの状態に戻す
				this.mWnd.setDefaultCursor();

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

				return true;
			}
			else
			{
				SGUtility.showMessageDialog(
					this.mWnd,
					"Click the figure.",
					"Failed to add a symbol.", 
					JOptionPane.WARNING_MESSAGE );

				return false;
			}

		}


		// a symbol of siginificant difference
		if( this.mWnd.mAddSignificantSymbolButton.isSelected() )
		{
			final boolean flag = this.mGraphElement.addSignificantDifferenceSymbol(x,y);
			if( flag )
			{
				// カーソルをデフォルトの状態に戻す
				this.mWnd.setDefaultCursor();

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

				return true;
			}
			else
			{
				SGUtility.showMessageDialog(
					this.mWnd,
					"Click the figure.",
					"Failed to add a symbol.", 
					JOptionPane.WARNING_MESSAGE );

				return false;

			}

		}


		return true;

	}




	/**
	 * 
	 * @param e
	 * @return true:有効な場所がクリックされた
	 */
	protected boolean onMouseClicked( MouseEvent e )
	{

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


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

		boolean flag = this.onFigureElementClicked(e);
		if( flag )
		{
			this.afterClicked(e);
			return true;
		}


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

			this.afterClicked(e);

			// マウス右ボタンクリックでポップアップメニューを出す
			if( (SwingUtilities.isRightMouseButton(e) )
				&& ( e.getClickCount() == 1 ) )
			{
				// update pop-up menu
				this.updatePopupMenu();

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

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

			return true;

		}

		return false;

	}



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

		return true;
	}



	/**
	 * 
	 */
	private boolean onFigureElementClicked( MouseEvent e )
	{
		SGIFigureElement[] array = this.getIFigureElementArrayFromLayer();
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii].onMouseClicked(e) )
			{
				return true;
			}
		}

		return false;
	}



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

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


		boolean flag;

		// SGIFigureElementに問い合わせ
		flag = this.onFigureElementPressed(e);
		if( flag )
		{
			this.afterPressed(e);
			return true;
		}


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

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

		if( flag )
		{
			this.afterPressed(e);
			this.updateFocusedFigureList(e);

			return true;
		}

		return false;
	}



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


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


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


		//
//		this.updateFocusedFigureList(e);


		return true;

	}




	/**
	 * 
	 * @return
	 */
	protected boolean updateFocusedFigureList( final MouseEvent e )
	{

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

		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 )
				{
					// if no figure element is pressed, remove this figure
					// from the list of the selected figure
					if( this.mPressedElement==null )
					{
						fList.remove(this);
					}
				}
			}
			else
			{
				fList.add(this);
			}
		}


		return true;
	}




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



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

		return true;
	}



	/**
	 * 
	 */
	private boolean onFigureElementPressed( MouseEvent e )
	{
		SGIFigureElement[] array = this.getIFigureElementArrayFromLayer();
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii].onMousePressed(e) )
			{
				this.mPressedElement = array[ii];
				return true;
			}
		}


		return false;
	}



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

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


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


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


		// 矩形を更新
		this.mRubberBandRect.setRect( x, y, w, h );


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


		return true;

	}



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

		// parallel displacement
		if( this.isGraphAreaContains( this.mPressedPoint ) )
		{
			this.mWnd.moveFocusedObjects( e.getX(), e.getY() );
			return true;
		}

		return false;
	}



	/**
	 * 
	 * @param dx
	 * @param dy
	 * @return
	 */
	public boolean translate( final int dx, final int dy )
	{
		// set the location of the rectangle for dragging
		Rectangle2D dRect = this.getRectangleOnDragging();
		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.setGraphAreaRectOnDragging();
		}

		return true;
	}



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



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

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


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


		// set the mouse cursor
		this.setMouseCursor(x,y);


		// set the rectangle of the graph area
		if( SGFigure.mRubberBandVisibleFlag )
		{
			this.setGraphAreaRectOnDragging();
		}


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


		return true;
	}




	/**
	 * 
	 */
	protected boolean isFigureMoved()
	{
		final boolean flag = !( this.mTempFigureRect.equals( this.getGraphAreaRect() ) );
		return flag;
	}



	/**
	 * 
	 * @return
	 */
	protected boolean drawbackFigure()
	{
		final Rectangle2D cRect = this.mWnd.mClientRect;

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

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


	/**
	 * 
	 */
	protected boolean onKeyReleased( final KeyEvent e )
	{
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii].onKeyReleased(e) )
			{
				return true;
			}
		}

		return true;
	}


	/**
	 * 
	 */
	protected boolean onKeyTyped( final KeyEvent e )
	{
//		this.typed(e);

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

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


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

		return true;
	}



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

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

		this.setGraphAreaRectToFigureElement();

		return true;
	}



	/**
	 * 
	 *
	 */
	public void notifyToRoot()
	{
		
	}




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



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

	}



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

		final float radius = 1.50f*SGDrawingWindow.mAnchorSize;

		final Rectangle2D rect = this.getGraphAreaRect();


		// 左上
		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 void 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 );
		}
		else
		{
			this.mWnd.setDefaultCursor();
		}

	}




	/**
	 * 
	 */
	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()
	{
		mPopupMenu.setBounds( 0, 0, 100, 100 );


		// draw rectangle
		{
			final JCheckBoxMenuItem item
				= new JCheckBoxMenuItem( MENUCMD_RUBBER_BANDING );
			item.setSelected( SGFigure.mRubberBandVisibleFlag );
			item.addActionListener(this);
			this.mPopupMenu.add(item);
		}


		// show bounding box
		{
			final JCheckBoxMenuItem item
				= new JCheckBoxMenuItem( MENUCMD_SHOW_BOUNDING_BOX );
			item.addActionListener(this);
			this.mPopupMenu.add(item);
		}


		this.mPopupMenu.addSeparator();

		// save property
		{
			final JMenuItem item
				= new JMenuItem( MENUCMD_SAVE_PROPERTY );
			item.addActionListener(this);
			this.mPopupMenu.add(item);
		}

		this.mPopupMenu.addSeparator();

		// remove figure
		{
			final JMenuItem item
				= new JMenuItem( MENUCMD_DELETE );
			item.addActionListener(this);
			this.mPopupMenu.add(item);
		}

		this.mPopupMenu.addSeparator();


		// property
		{
			final JMenuItem item
				= new JMenuItem( MENUCMD_PROPERTY );
			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()
	{

		final SGFigureDialog dg = (SGFigureDialog)this.mDialog;

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

		dg.setFigureBackgroundColor( this.mBackgroundColor );

		dg.setFigureTransparentFlag( this.mTransparentFlag );

		dg.setFigureSpaceLineAndNumber( this.mSpaceAxisLineAndNumber*SGIConstants.CM_POINT_RATIO );
		dg.setFigureSpaceNumberAndTitle( this.mSpaceNumberAndTitle*SGIConstants.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.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();
		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("<< 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_DELETE ) )
		{
			this.mWnd.hideSelectedFigures();
		}
		else if( command.equals( MENUCMD_SAVE_PROPERTY ) )
		{
			this.mWnd.createPropertyFileForFocusedFigures();
		}
		else if( command.equals( MENUCMD_PROPERTY ) )
		{
			this.getDialog().setVisible(true);
		}


		//
		// 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 )
		{
			this.fromFigureElement(e);
		}


		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;

	}



	/**
	 * 
	 */
	private boolean fromFigureElement( final ActionEvent e )
	{
//System.out.println("<< SGFigure::fromFigureElement >>");
//System.out.println(e);
//System.out.println(e.getID());
//System.out.println();


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

		final int id = e.getID();

		switch( id )
		{

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

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


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


				break;

			}


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

				break;
			}


			// Add to the last of history list
			case SGFigure.ADD_LATEST_HISTORY :
			{
				this.addToLatestHistory( element );

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

				break;
			}


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

				break;
			}


			//
			case SGFigure.FOCUSED_OBJECTS_CHANGED :
			{
//System.out.println(e);
				this.mWnd.updateDataItem();
				break;
			}


			//
			case SGFigure.NOTIFY_CHANGE_TO_ROOT :
			{
				this.mWnd.updateHistoryTree();
				break;
			}


			default :
			{
				
			}

		}


		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() );
this.mCompletedFlag = true;
		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.updateHistory();
this.notifyToRoot();

		}


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


		return true;

	}



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

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

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


		this.updateThisObjectHistory();


		return true;

	}


	/**
	 * 
	 * @return
	 */
	public boolean updateThisObjectHistory()
	{
		// オブジェクト履歴を更新
		this.updateObjectHistory(this);


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


		return true;
	}




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



	/**
	 * 
	 * @return
	 */
	protected boolean addToLatestHistory( SGIFigureElement el )
	{
		ArrayList list = this.mUndoableObjectHistoryList;
		ArrayList objList = (ArrayList)list.get( list.size() - 1 );
		objList.add(el);
		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.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.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)
	{

		if( !this.mCompletedFlag )
		{
			return;
		}

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


	/**
	 * 
	 * @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( SGFigure.mBoundingBoxVisibleFlag )
		{
			Rectangle2D rect = this.getBoundingBox();
			if( rect!=null )
			{
				g2d.setPaint( Color.BLUE );
				g2d.setStroke( new BasicStroke(1) );
				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 = this.mGraphAreaX - (float)( bb.getX() )/this.mMagnification;
		final float y = this.mGraphAreaY - (float)( bb.getY() )/this.mMagnification;
		this.writeProperty_( writer, x, y );
		writer.write("\n\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() );
		SGUtilityText.writePropertyLine( writer, PF_FIGURE_X_IN_CLIENT, new Float( x*SGIConstants.CM_POINT_RATIO ) );
		SGUtilityText.writePropertyLine( writer, PF_FIGURE_Y_IN_CLIENT, new Float( y*SGIConstants.CM_POINT_RATIO ) );
		SGUtilityText.writePropertyLine( writer, PF_WIDTH, new Float( this.mGraphAreaWidth*SGIConstants.CM_POINT_RATIO ) );
		SGUtilityText.writePropertyLine( writer, PF_HEIGHT, new Float( this.mGraphAreaHeight*SGIConstants.CM_POINT_RATIO ) );
		SGUtilityText.writePropertyLine( writer, PF_SPACE_AXIS_LINE_AND_NUMBER, new Float( this.mSpaceAxisLineAndNumber*SGIConstants.CM_POINT_RATIO ) );
		SGUtilityText.writePropertyLine( writer, PF_SPACE_NUMBER_AND_TITLE, new Float( this.mSpaceNumberAndTitle*SGIConstants.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 FigureProperties readProperties(
		final BufferedReader br )
		throws IOException, ClassNotFoundException,
			InstantiationException, IllegalAccessException
	{

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

		FigureProperties p = new FigureProperties();


		String line = null;
		float unit = SGIConstants.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 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.mGraphAreaX - fp.x )*SGIConstants.CM_POINT_RATIO;
		final float diffY = Math.abs( this.mGraphAreaY - fp.y )*SGIConstants.CM_POINT_RATIO;
		final float diffW = Math.abs( this.mGraphAreaWidth - fp.width )*SGIConstants.CM_POINT_RATIO;
		final float diffH = Math.abs( this.mGraphAreaHeight - fp.height )*SGIConstants.CM_POINT_RATIO;
		final float eps = (float)SGUtilityNumber.getPowersOfTen(MINIMAL_LENGTH_ORDER);
		if( diffX > eps )
		{
			this.mGraphAreaX = fp.x;
		}
		if( diffY > eps )
		{
			this.mGraphAreaY = fp.y;
		}
		if( diffW > eps )
		{
			this.mGraphAreaWidth = fp.width;
		}
		if( diffH > eps )
		{
			this.mGraphAreaHeight = fp.height;
		}

//		this.mSpaceAxisLineAndNumber = fp.spaceLineAndNumber;
//		this.mSpaceNumberAndTitle = fp.spaceNumberAndTitle;

this.setSpaceAxisLineAndNumber( fp.spaceLineAndNumber*this.mMagnification );
this.setSpaceNumberAndTitle( fp.spaceNumberAndTitle*this.mMagnification );

		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;

	}



}
