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

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

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

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


/**
 * A figure.
 */

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


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


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


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


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


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


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


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



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


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


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


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


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


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


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


	/**
	 * 
	 */
	protected Color mBackgroundColor;


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


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


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


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


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


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


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



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


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


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


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


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


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


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


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



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



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


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


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

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

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


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



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



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



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

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

		return true;
	}



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

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


	private JComponent mComponent = null;

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

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


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


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





	//
	// setters and getters
	//


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


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



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


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


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


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



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

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


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


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



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



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



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



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



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


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


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


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

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

		return str;
	}



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

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



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


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


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


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

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

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



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



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


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


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

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


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



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



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



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



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


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


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


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


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


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


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


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

		return (diff<ten);
	}


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



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



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

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

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

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

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

		this.updateGraphRect();

		return true;
	}



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


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

		final Rectangle2D cRect = this.mWnd.getPaperRect();

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

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

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


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


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

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


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



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


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



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

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

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

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


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

		return true;
	}



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



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


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



	public boolean addData(
		final SGData data, final String name, final SGIFigureElement[] array )
	{
		this.addToList(data);
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii].addData( data, name );
		}
		this.repaint();
		return true;		
	}


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


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

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

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

		this.broadcast( this.getIFigureElement( SGIGraphElement.class ) );

		return true;
	}



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


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




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

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

		return true;
	}



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

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

		//
		this.updateGraphRect();

		//
		this.setViewBounds();

		return true;

	}



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

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


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


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

		return true;
	}



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



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


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




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


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

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

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

		return false;
	}




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


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


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


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



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



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

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

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

			return true;
		}


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

			// aftertreatment
			this.afterClicked(e);

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

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

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

			return true;

		}

		return false;

	}

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


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


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


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


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



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

		return true;
	}



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



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

		return null;
	}



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

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

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

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

		return false;
	}


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

		return false;		
	}


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


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

		// aftertreatment 
		this.afterPressed(e);

		return true;
	}



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

		// set visible the rubber band
		SGFigure.mRubberBandVisibleFlag = true;


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


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


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


		return true;

	}




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

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

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

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

		return true;
	}




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


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

		return null;
	}



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

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

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

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

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


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


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

		// other points
		if( ml == OTHER )
		{
		
//			Rectangle2D rb = this.getRubberBandRect();
//			if( rb.contains(x,y) )
			{
				// parallel displacement
				this.mWnd.moveFocusedObjects(x,y);
				return true;
			}
		}
		else
		{

			// record the temporary bounds
			this.recordFigureRect();

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

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

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

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


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


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

		return false;
	}



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

		this.snapToLines( OTHER );

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

	}


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

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

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

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


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


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

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


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

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

		return true;
	}


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


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


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


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


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

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

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

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

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

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

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

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

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

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


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


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

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

			int nNew = nMax;
			if( ( maxX - px ) - rMax > interval/2.0f )
			{
				nNew++;
			}
			
			final float maxNew = px + nNew*interval;
			wNew = maxNew - minX;
		}
		else if( mouseLocation == OTHER )
		{
			xNew = oxNew;
			wNew = (float)dRect.getWidth();
		}
		else
		{
			xNew = minX;
			wNew = (float)dRect.getWidth();
		}


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

			int nNew = nMin;
			if( ( minY - py ) - rMin > interval/2.0f )
			{
				nNew++;
			}
			
			final float minNew = py + nNew*interval;
			hNew = maxY - minNew;

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


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

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

	}



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

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

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

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

		return true;
	}




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



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


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

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

		final int margin = DRAW_BACK_MARGIN;

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

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


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

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


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

		return true;
	}



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




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

		this.updateGraphRect();

		return true;
	}



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

		this.updateGraphRect();

		return true;
	}



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

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

		this.updateImage();

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

		this.repaint();

	}



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


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


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

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



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

		return null;
	}



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



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



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

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


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

		return cur;
	}


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



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

		this.mWnd.setCursor( Cursor.getDefaultCursor() );

		return false;
	}


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



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


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


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


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



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

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

		JCheckBoxMenuItem item;

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

		// snap to grid
		item = SGUtility.addCheckBoxItem( p, this, MENUBARCMD_SNAP_TO_GRID );
		item.setSelected( SGFigure.mSnapToGridFlag );
		
		// show bounding box
		item = SGUtility.addCheckBoxItem( p, this, MENUCMD_SHOW_BOUNDING_BOX );
		item.setSelected( SGFigure.mBoundingBoxVisibleFlag );

		p.addSeparator();


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

		p.addSeparator();

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


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

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

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

		p.addSeparator();


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

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

		p.addSeparator();

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

		return true;
	}



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



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


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


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

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

		return true;
	}



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



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

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

		{
			//
			// popup menu
			//

			if( command.equals( MENUCMD_SHOW_BOUNDING_BOX ) )
			{
				mBoundingBoxVisibleFlag = !mBoundingBoxVisibleFlag;
				this.updatePopupMenu();
				this.updateImage();
				this.repaint();
				return;
			}
			else if( command.equals( MENUCMD_RUBBER_BANDING ) )
			{
				SGFigure.mRubberBandFlag = !SGFigure.mRubberBandFlag;
				this.updatePopupMenu();
				this.updateImage();
				this.repaint();
				return;
			}
			else if( command.equals( MENUBARCMD_SNAP_TO_GRID ) )
			{
				SGFigure.setSnappingToGrid( !SGFigure.isSnappingToGrid() );
				this.updatePopupMenu();
				this.mWnd.updateSnapToGridItems();
				return;
			}
			else if( command.equals( MENUCMD_SAVE_PROPERTY ) )
			{
				this.mWnd.createPropertyFileFromFocusedFigures();
			}
			else if( command.equals( MENUCMD_PROPERTY ) )
			{
				this.mWnd.showPropertyDialogForSelectedFigures();
			}
			else if( command.equals( MENUCMD_MOVE_TO_FRONT ) )
			{
				this.mWnd.moveFocusedObjectsToFront();
			}
			else if( command.equals( MENUCMD_MOVE_TO_BACK ) )
			{
				this.mWnd.moveFocusedObjectsToBack();
			}
			else if( command.equals( MENUCMD_CUT ) )
			{
				this.mWnd.cutFocusedObjects();
			}
			else if( command.equals( MENUCMD_COPY ) )
			{
				this.mWnd.copyFocusedObjects();
			}
			else if( command.equals( MENUCMD_PASTE ) )
			{
				this.mWnd.pasteCopiedObjects();
			}
			else if( command.equals( MENUCMD_DELETE ) )
			{
				this.mWnd.deleteFocusedObjects();
			}
			else if( command.equals( MENUCMD_DUPLICATE ) )
			{
				this.mWnd.duplicateFocusedObjects();
			}

		}



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

		if( source instanceof SGIFigureElement )
		{

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

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

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

//				this.getComponent().repaint();
			}
			// 共通プロパテイ設定
			else if( command.equals( SGFigure.SET_PROPERTIES_OF_SELECTED_OBJECTS ) )
			{
				//
				this.mWnd.showPropertyDialogForSelectedObjects( this, element );
			}
			else if( command.equals( SGFigure.CLEAR_FOCUSED_OBJECTS ) )
			{
				this.mWnd.clearAllFocusedObjectsInFigures();
			}
			//
			else if( command.equals( SGFigure.NOTIFY_CHANGE_TO_ROOT ) )
			{
				this.notifyToRoot();
			}
			else if( command.equals( SGFigure.NOTIFY_CHANGE_CURSOR ) )
			{
				this.setCursorToWindow(element);
			}

		}

		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 SGUndoManager mUndoManager = new SGUndoManager( this );

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


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


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



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

		this.mTemporaryProperties = null;

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

		return true;
	}


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

		return true;
	}


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

		this.updateGraphRect();

		this.mTemporaryProperties = null;

		return true;
	}




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


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


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

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

		ArrayList list = new ArrayList();
		SGIFigureElement[] array = this.getIFigureElementArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			SGIFigureElement element = array[ii];
			list.add(element);
			
			// particular case
			if( element.equals( this.getLegendElement() ) )
			{
//				element.setChanged(true);
			}
		}

		this.mUndoManager.updateHistory(list);

		return true;
	}


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

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



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


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


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


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


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

		return true;
	}



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

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

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

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


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

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

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

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

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

		return true;
	}




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

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

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

		return rect;
	}


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


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

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

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

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

		return true;
	}



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

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

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

		return true;
	}



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



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



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

				return true;
			}
		}

		return true;
	}



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



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



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


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


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


//	protected BufferedImage mImg = null;
	
	
	/**
	 * 
	 */	
	public void paint( final Graphics g, final boolean clip )
	{
		this.paintGraphics( (Graphics2D)g, clip );

//		if( this.mImg!=null )
//		{	
//System.out.println("paint");
//			Graphics2D g2d = (Graphics2D)g;
//			g2d.drawImage( this.mImg, null, this.getComponent() );
//		}
	}
	
	
	/**
	 * Update the bufferred image.
	 */
	protected void updateImage()
	{
//		final int w = this.getComponent().getWidth();
//		final int h = this.getComponent().getHeight();
//	
//		if( w<=0 | h<=0 )
//		{
//			return;
//		}
//
//		BufferedImage bImg = new BufferedImage( w, h, BufferedImage.TYPE_INT_ARGB );
//	
//		this.paintGraphics( bImg.createGraphics(), true );
//		this.mImg = bImg;
	}



	/**
	 * 
	 * @param g
	 * @param clip
	 */
	private void paintGraphics( Graphics2D g2d, boolean clip )
	{
		
		// fill the background
		if( this.mTransparentFlag == false )
		{
			final Rectangle2D graphAreaRect = this.getGraphRect();
			g2d.setPaint(this.mBackgroundColor);
			g2d.fill(graphAreaRect);
		}

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

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



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

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

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

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

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

		return el;
	}



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

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

		return el;
	}

	

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

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

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


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

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


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

	private final float DUPLICATION_OFFSET = 20.0f;

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



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

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

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

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

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

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

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

		return true;
	}



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

		FigureProperties fp = (FigureProperties)p;

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

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

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

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

		return true;
	}



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

		return true;
	}



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

		return list;
	}


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

		return list;
	}



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

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

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

		return true;
	}


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

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

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

		return true;
	}


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


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



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

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

		return map;
	}



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

		return true;
	}



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




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

		public FigureProperties()
		{
			super();
		}

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

			FigureProperties p = (FigureProperties)obj;

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

			return true;

		}


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

		public void setY( final float value )
		{
			this.mY = value;
		}
		
		public void setWidth( final float value )
		{
			this.mWidth = value;
		}
		
		public void setHeight( final float value )
		{
			this.mHeight = value;
		}

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

		public void setSpaceNumberAndTitle( final float value )
		{
			this.mSpaceNumberAndTitle = value;
		}
		
		public void setBackgroundColor( final Color cl )
		{
			this.mBackgroundColor = cl;
		}
		
		public void setTransparent( final boolean b )
		{
			this.mTransparentFlag = b;
		}

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

		public float getX()
		{
			return this.mX;
		}
		
		public float getY()
		{
			return this.mY;
		}
		
		public float getWidth()
		{
			return this.mWidth;
		}
		
		public float getHeight()
		{
			return this.mHeight;
		}
		
		public float getSpaceLineAndNumber()
		{
			return this.mSpaceLineAndNumber;
		}

		public float getSpaceNumberAndTitle()
		{
			return this.mSpaceNumberAndTitle;
		}
		
		public Color getBackgroundColor()
		{
			return this.mBackgroundColor;
		}
		
		public boolean isTransparent()
		{
			return this.mTransparentFlag;
		}
		
		public boolean isLegendVisible()
		{
			return this.mLegendVisibleFlag;
		}


	}



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


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

}

