
package jp.riken.brain.ni.samuraigraph.figure.java2d;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

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

import jp.riken.brain.ni.samuraigraph.base.SGAxis;
import jp.riken.brain.ni.samuraigraph.base.SGDrawingElement;
import jp.riken.brain.ni.samuraigraph.base.SGIAxisBreakElement;
import jp.riken.brain.ni.samuraigraph.base.SGIAxisElement;
import jp.riken.brain.ni.samuraigraph.base.SGIConstants;
import jp.riken.brain.ni.samuraigraph.base.SGICopiable;
import jp.riken.brain.ni.samuraigraph.base.SGIDisposable;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElement;
import jp.riken.brain.ni.samuraigraph.base.SGIGraphElement;
import jp.riken.brain.ni.samuraigraph.base.SGIGridElement;
import jp.riken.brain.ni.samuraigraph.base.SGILegendElement;
import jp.riken.brain.ni.samuraigraph.base.SGIMovable;
import jp.riken.brain.ni.samuraigraph.base.SGINode;
import jp.riken.brain.ni.samuraigraph.base.SGIPropertyDialogObserver;
import jp.riken.brain.ni.samuraigraph.base.SGISelectable;
import jp.riken.brain.ni.samuraigraph.base.SGIShapeElement;
import jp.riken.brain.ni.samuraigraph.base.SGISignificantDifferenceElement;
import jp.riken.brain.ni.samuraigraph.base.SGIStringElement;
import jp.riken.brain.ni.samuraigraph.base.SGITimingLineElement;
import jp.riken.brain.ni.samuraigraph.base.SGIUndoable;
import jp.riken.brain.ni.samuraigraph.base.SGProperties;
import jp.riken.brain.ni.samuraigraph.base.SGPropertyDialog;
import jp.riken.brain.ni.samuraigraph.base.SGUndoManager;
import jp.riken.brain.ni.samuraigraph.base.SGUtility;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityNumber;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityText;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementArrow;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementLine;
import jp.riken.brain.ni.samuraigraph.figure.SGFigureElement;
import jp.riken.brain.ni.samuraigraph.figure.SGIArrowConstants;
import jp.riken.brain.ni.samuraigraph.figure.SGIDefaultValuesShape;
import jp.riken.brain.ni.samuraigraph.figure.SGIRectangleConstants;
import jp.riken.brain.ni.samuraigraph.figure.SGISymbolConstants;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementArrow.ArrowProperties;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementRectangle.RectangleProperties;

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


/**
 *
 */
public class SGShapeElement extends SGFigureElement
	implements SGIShapeElement, SGIDefaultValuesShape
{


	/**
	 * 
	 */
	private SGIAxisElement mAxisElement = null;

	
	/**
	 * 
	 */
	private SGRectangularShapeDialog mRectangularShapeDialog = null;


	/**
	 * 
	 */
	private SGArrowDialog mArrowDialog = null;


	/**
	 * 
	 */
	public SGShapeElement()
	{
		super();
	}



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


	/**
	 * 
	 */
	public void dispose()
	{
		super.dispose();

		this.mAxisElement = null;
		this.mRectangularShapeDialog.dispose();
		this.mRectangularShapeDialog = null;
		this.mArrowDialog.dispose();
		this.mArrowDialog = null;
	}


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


	/**
	 * 
	 */
	public boolean setDialogOwner( final Frame frame )
	{
		super.setDialogOwner(frame);
		this.createDialog();
		return true;
	}


	/**
	 * 
	 */
	public String toString()
	{
		return "SGShapeElement";
	}


	/**
	 * create a dialog
	 */
	private boolean createDialog()
	{
		// rectangle
		final SGRectangularShapeDialog rdg
			= new SGRectangularShapeDialog( this.mDialogOwner, true );
		this.mRectangularShapeDialog = rdg;

		// arrow
		final SGArrowDialog adg
			= new SGArrowDialog( this.mDialogOwner, true );
		this.mArrowDialog = adg;

		return true;
	}


	/**
	 * 
	 */
	public boolean synchronize(SGIFigureElement element)
	{

		boolean flag = true;
		if( element instanceof SGIGraphElement )
		{
			
		}
		else if( element instanceof SGIStringElement )
		{
			
		}
		else if( element instanceof SGILegendElement )
		{
			
		}
		else if( element instanceof SGIAxisElement )
		{
			flag = this.synchronizeToAxisElement( (SGIAxisElement)element );
		}
		else if( element instanceof SGIAxisBreakElement )
		{
			
		}
		else if( element instanceof SGISignificantDifferenceElement )
		{
			
		}
		else if( element instanceof SGITimingLineElement )
		{
			
		}
		else if( element instanceof SGIGridElement )
		{

		}
		else if( element instanceof SGIShapeElement )
		{

		}
		else
		{
			flag = element.synchronizeArgument( this );
		}


		return flag;
	}



	private boolean synchronizeToAxisElement( SGIAxisElement element )
	{
		ArrayList list = this.getVisibleChildList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			ShapeObject sh = (ShapeObject)list.get(ii);
			sh.setShapeWithAxesValues();
		}
		
		return true;
	}


	/**
	 * Synchronize the element given by the argument.
	 * @param element An object to be synchronized.
	 * @return
	 */
	public boolean synchronizeArgument( final SGIFigureElement element )
	{
		// this shouldn't happen
		throw new Error();
	}



	/**
	 * 
	 */
	public boolean addShape( final int type, final int x, final int y )
	{
		if( this.getGraphRect().contains( x, y ) == false )
		{
			return false;
		}

		SGAxis xAxis = this.mAxisElement.getAxis( SHAPE_HORIZONTAL_AXIS );
		SGAxis yAxis = this.mAxisElement.getAxis( SHAPE_PERPENDICULAR_AXIS );

		final float mag = this.getMagnification();
		if( type == RECTANGLE )
		{
			return this.addRectangle( xAxis, yAxis, x, y );
		}
		else if( type == ELLIPSE )
		{
			return this.addEllipse( xAxis, yAxis, x, y );
		}
		else if( type == ARROW )
		{
			final int xx = x + (int)(mag*50);
			final int yy = y + (int)(mag*40);
			return this.addArrow( xAxis, yAxis, x, y, xx, yy );
		}
		else if( type == LINE )
		{
			final int xx = x + (int)(mag*50);
			final int yy = y + (int)(mag*40);
			return this.addLine( xAxis, yAxis, x, y, xx, yy );
		}

		return false;
	}


	//
	private boolean addRectangle(
			final SGAxis xAxis, final SGAxis yAxis, final int x, final int y )
	{
		Rect el = new Rect();
		el.zoom( this.getMagnification() );

		ShapeObject sh = new ShapeObject(el,xAxis,yAxis);
		el.setShape(sh);

		el.setLocation( x, y );
		el.setAxisValuesWithShape();

		this.addShape( sh );

		return true;
	}



	//
	private boolean addEllipse(
		final SGAxis xAxis, final SGAxis yAxis, final int x, final int y )
	{
		Ellipse el = new Ellipse();
		el.zoom( this.getMagnification() );

		ShapeObject sh = new ShapeObject(el,xAxis,yAxis);
		el.setShape(sh);

		el.setLocation( x, y );
		el.setAxisValuesWithShape();

		this.addShape( sh );

		return true;
	}


	//
	private boolean addArrow(
		final SGAxis xAxis, final SGAxis yAxis,
		final int sx, final int sy,
		final int ex, final int ey )
	{
		Arrow el = new Arrow();
		return this.addArrow(
			el,
			SHAPE_ARROW_START_HEAD_TYPE,
			SHAPE_ARROW_END_HEAD_TYPE,
			xAxis, yAxis, sx, sy, ex, ey );
	}


	//
	private boolean addLine(
		final SGAxis xAxis, final SGAxis yAxis,
		final int sx, final int sy,
		final int ex, final int ey )
	{
		Arrow el = new Arrow();
		return this.addArrow(
			el,
			SGISymbolConstants.SYMBOL_TYPE_VOID,
			SGISymbolConstants.SYMBOL_TYPE_VOID,
			xAxis, yAxis, sx, sy, ex, ey );
	}


	//
	private boolean addArrow(
		final Arrow el,
		final int startType,
		final int endType,
		final SGAxis xAxis, final SGAxis yAxis,
		final int sx, final int sy,
		final int ex, final int ey )
	{
		ShapeObject sh = new ShapeObject(el,xAxis,yAxis);
		el.zoom( this.getMagnification() );
		el.setShape(sh);
		el.setStartHeadType( startType );
		el.setEndHeadType( endType );
		el.setStartX(sx);
		el.setStartY(sy);
		el.setEndX(ex);
		el.setEndY(ey);

		el.setAxisValuesWithShape();

		this.addShape( sh );

		return true;
	}



	//
	private boolean addShape( final ShapeObject sh )
	{
		//
		this.addToList(sh);

		//
		this.setChanged(true);

		// initialize history
		sh.initPropertiesHistory();

		this.notifyToRoot();

		return true;
	}


	// couner for ID number
	private int mIDCounter = 0;


	// add object to the list
	private void addToList( ShapeObject el )
	{
		this.mChildList.add( el );

		el.setID( this.mIDCounter );
		this.mIDCounter++;
	}



	// clear other type focused objects
	private boolean clearFocusedObjectOtherThan( ShapeObject sh )
	{
		Class cl = sh.getIElement().getClass();
		ArrayList list = this.getFocusedObjectsList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			ShapeObject el = (ShapeObject)list.get(ii);
			Class cl_ = el.getIElement().getClass();

			// if one of two classes is not the subclass of another class,
			// clear the focus
			if( !cl_.isAssignableFrom(cl) & !cl.isAssignableFrom(cl_) )
			{
				el.setSelected(false);
			}
		}

		return true;
	}



	/**
	 * Zoom this object.
	 */
	public boolean zoom( final float mag )
	{
		super.zoom(mag);

		ArrayList list = this.mChildList;
		for( int ii=0; ii<list.size(); ii++ )
		{
			final ShapeObject sh = (ShapeObject)list.get(ii);
			sh.zoom(mag);
		}

		return true;
	}



	/**
	 * 
	 */
	public boolean setGraphRect(
		final float x, final float y, final float width, final float height )
	{
		if( super.setGraphRect(x,y,width,height) == false )
		{
			return false;
		}

		ArrayList list = this.mChildList;
		for( int ii=0; ii<list.size(); ii++ )
		{
			final ShapeObject el = (ShapeObject)list.get(ii);
			el.setShapeWithAxesValues();			
		}

		return true;
	}


	/**
	 * 
	 */
	public SGProperties getProperties()
	{
		ShapeElementProperties p = new ShapeElementProperties();
		p.visibleShapeList = this.getVisibleChildList();
		return p;
	}



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

		ShapeElementProperties wp = (ShapeElementProperties)p;
		final boolean flag = this.setVisibleChildList( wp.visibleShapeList );
		if( !flag )
		{
			return false;
		}

		return true;
	}


	/**
	 * 
	 */
	public boolean setMementoBackward()
	{
		boolean flag = super.setMementoBackward();
		if( !flag )
		{
			return false;
		}

		this.clearFocusedObjects();
		this.notifyChange();

		return true;
	}



	/**
	 * 
	 */
	public boolean setMementoForward()
	{
		boolean flag = super.setMementoForward();
		if( !flag )
		{
			return false;
		}

		this.clearFocusedObjects();
		this.notifyChange();

		return true;
	}



	/**
	 * Create copies of the focused objects.
	 * @return
	 */
	public boolean duplicateFocusedObjects()
	{
		final int ox = (int)( this.mMagnification*OFFSET_DUPLICATED_OBJECT_X );
		final int oy = (int)( this.mMagnification*OFFSET_DUPLICATED_OBJECT_Y );

		ArrayList cList = this.duplicateObjects();
		for( int ii=0; ii<cList.size(); ii++ )
		{
			// duplicate
			ShapeObject el = (ShapeObject)cList.get(ii);

			// translate the duplicate
			el.translate( ox, oy );

			// set selected
			el.setSelected(true);

			// add to the list
			this.addToList(el);

			// initialize history
			el.initPropertiesHistory();

			int a=0;
			a++;
		}

		if( cList.size()!=0 )
		{
			this.setChanged(true);
		}
		
//		this.repaint();

		return true;
	}



	/**
	 * Paste the objects.
	 * @param list of the objects to be pasted
	 * @return true:succeeded, false:failed
	 */
	public boolean paste( ArrayList list )
	{
		final float mag = this.getMagnification();
		final int ox = (int)( mag*OFFSET_DUPLICATED_OBJECT_X );
		final int oy = (int)( mag*OFFSET_DUPLICATED_OBJECT_Y );

		int cnt = 0;
		for( int ii=0; ii<list.size(); ii++ )
		{
			Object obj = list.get(ii);
			if( obj instanceof ShapeObject )
			{
				ShapeObject shOld = (ShapeObject)obj;

				// translate the instance to be pasted
				shOld.translate( ox, oy );

				SGProperties p = shOld.getMemento();
				
				IElement elCopy = (IElement)shOld.getIElement().copy();
				
				ShapeObject shNew = new ShapeObject(elCopy);
				elCopy.setShape(shNew);
				shNew.setMagnification(mag);
				shNew.setMemento(p);

				shNew.setXAxis( this.mAxisElement.getAxisInPlane( shOld.mTempXAxis ) );
				shNew.setYAxis( this.mAxisElement.getAxisInPlane( shOld.mTempYAxis ) );

				shNew.setShapeWithAxesValues();

				// add to the list
				this.addToList(shNew);

				// initialize history
				shNew.initPropertiesHistory();

				cnt++;
			}
		}
		
		if( cnt!=0 )
		{
			this.setChanged(true);
		}

//		this.repaint();

		return true;
	}


	/**
	 * 
	 */
	public String getTagName()
	{
		return TAG_NAME_SHAPE;
	}


	/**
	 * 
	 */
	public boolean writeProperty( final Element el )
	{
		return true;
	}


	/**
	 * 
	 */
	public boolean onMouseClicked( final MouseEvent e )
	{
		ArrayList list = this.getVisibleChildList();
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final ShapeObject el = (ShapeObject)list.get(ii);
			if( el.isValid() == false )
			{
				continue;
			}
			if( this.clickDrawingElements(el,e) )
			{
				return true;
			}
		}

		return false;
	}


	/**
	 * 
	 * @return
	 */
	public ArrayList getPropertyDialogObserverList()
	{
		ArrayList list = this.getFocusedObjectsList();
		ArrayList obsList = new ArrayList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			ShapeObject obj = (ShapeObject)list.get(ii);
			obsList.add( obj.getIElement() );
		}

		return obsList;
	}


	/**
	 * 
	 */
	private boolean clickDrawingElements(
		final ShapeObject el, final MouseEvent e )
	{
		final int x = e.getX();
		final int y = e.getY();
		final int cnt = e.getClickCount();

		// clicked on the line elements
		if( el.contains(x,y) )
		{
			this.updateFocusedObjectsList( el, e );

			if( SwingUtilities.isLeftMouseButton(e) & cnt==1 )
			{

			}
			else if( SwingUtilities.isLeftMouseButton(e) & cnt==2 )
			{
				this.setPropertiesOfSelectedObjects();
			}
			else if( SwingUtilities.isRightMouseButton(e) & cnt==1 )
			{
				el.getPopupMenu().show( this.getComponent(), x, y );
			}

			return true;
		}


		return false;
	}



	/**
	 * 
	 */
	public boolean onMousePressed( final MouseEvent e )
	{
		final int x = e.getX();
		final int y = e.getY();

		//
		ArrayList list = this.getVisibleChildList();
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final ShapeObject el = (ShapeObject)list.get(ii);
			if( el.isValid() == false )
			{
				continue;
			}
			if( el.press(e) )
			{
				this.mPressedPoint = e.getPoint();
				if( el.isSelected() )
				{
					this.mDraggableFlag = true;
				}
				return true;
			}
		}

		return false;
	}



	/**
	 * 
	 */
	public boolean onMouseDragged( final MouseEvent e )
	{
		if( this.mPressedPoint==null )
		{
			return false;
		}
		if( this.mDraggableFlag == false )
		{
			return false;
		}

		ArrayList list = this.getFocusedObjectsList();
		if( list.size()==1 )
		{
			ShapeObject el = (ShapeObject)list.get(0);
			if( el.drag(e) == false )
			{
				return false;
			}
			el.setAxisValuesWithShape();
		}
		else
		{
			final int dx = e.getX() - this.mPressedPoint.x;
			final int dy = e.getY() - this.mPressedPoint.y;

			for( int ii=0; ii<list.size(); ii++ )
			{
				ShapeObject el = (ShapeObject)list.get(ii);
				if( el.isValid() == false )
				{
					continue;
				}
				el.translate(dx,dy);
				el.setAxisValuesWithShape();
			}
		}
		
		this.mPressedPoint = e.getPoint();

		return true;
	}


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

		ArrayList list = this.getFocusedObjectsList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			ShapeObject el = (ShapeObject)list.get(ii);
			if( el.isValid() == false )
			{
				continue;
			}
			if( el.contains(x,y) )
			{
				this.setMouseCursor( Cursor.HAND_CURSOR );
			}
			else
			{
				this.setMouseCursor( Cursor.DEFAULT_CURSOR );
			}
		}

		this.mDraggableFlag = false;
		this.notifyToRoot();

		return false;
	}


	/**
	 * 
	 */
	public boolean onDrawingElement(int x, int y)
	{
		ArrayList list = this.getVisibleChildList();
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final ShapeObject el = (ShapeObject)list.get(ii);
			if( el.isValid() == false )
			{
				continue;
			}
			final boolean flag = el.contains(x,y);
			el.mFrameFlag = flag;

			if( flag )
			{
				if( el.isSelected() )
				{
					final int ml = el.getMouseLocation(x,y);
					el.mMouseLocation = ml;
					setMouseCursor( el.getCursor(ml) );
					return true;
				}
				else
				{
					setMouseCursor( Cursor.HAND_CURSOR );
					return true;
				}
			}
		}

		return false;
	}



	/**
	 * 
	 */
	public Element createElement( final Document document )
	{
		// create an Element object
		Element el = this.createThisElement( document );
		if( el==null )
		{
			return null;
		}

		// shape
		ArrayList list = this.getVisibleChildList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			ShapeObject sh = (ShapeObject)list.get(ii);
			if( sh.isValid() == false )
			{
				continue;
			}
			Element elShape = sh.createElement( document );
			if( elShape==null )
			{
				return null;
			}
			el.appendChild( elShape );
		}
		
		return el;
	}


	/**
	 * 
	 */
	public boolean readProperty( Element element )
	{
		NodeList nList = element.getChildNodes();
		for( int ii=0; ii<nList.getLength(); ii++ )
		{
			Node node = nList.item(ii);
			if( node instanceof Element )
			{
				Element el = (Element)node;
				ShapeObject sh = new ShapeObject();
				if( sh.readProperty(el) == false )
				{
					return false;
				}
				sh.initPropertiesHistory();
				this.addToList(sh);
			}
		}
		
		return true;
	}


	/**
	 * 
	 */
	public boolean setTemporaryPropertiesOfFocusedObjects()
	{
		ArrayList list = this.getFocusedObjectsList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			ShapeObject el = (ShapeObject)list.get(ii);
			el.mTemporaryProperties = el.getMemento();
		}
		return true;
	}


	/**
	 * 
	 */
	public boolean setChangedFocusedObjects()
	{
		ArrayList list = this.getFocusedObjectsList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			ShapeObject el = (ShapeObject)list.get(ii);
			SGProperties temp = el.mTemporaryProperties;
			if( temp!=null )
			{
				SGProperties p = el.getMemento();
				if( p.equals(temp)==false )
				{
					el.setChanged(true);
				}
			}
		}
		return true;
	}


	/**
	 * 
	 */
	public void paintGraphics( Graphics g, boolean clip )
	{
		Graphics2D g2d = (Graphics2D)g;

		ArrayList list = this.getVisibleChildList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			ShapeObject sh = (ShapeObject)list.get(ii);
			if( sh.isValid() == false )
			{
				continue;
			}
			sh.paintElement(g2d);
		}

		// draw symbols around all objects
		if( this.mSymbolsVisibleFlagAroundAllObjects )
		{
			for( int ii=0; ii<list.size(); ii++ )
			{
				ShapeObject sh = (ShapeObject)list.get(ii);
				if( sh.isValid() == false )
				{
					continue;
				}
				ArrayList pList = sh.getAnchorPointList();
				SGUtilityForFigureElement.drawAnchorAsChildObject( pList, g2d );
			}
		}

		// draw symbols around focused objects
		if( this.mSymbolsVisibleFlagAroundFocusedObjects )
		{
			ArrayList fList = new ArrayList();
			this.getFocusedObjectsList( fList );
			for( int ii=0; ii<fList.size(); ii++ )
			{
				ShapeObject sh = (ShapeObject)fList.get(ii);
				if( sh.isValid() == false )
				{
					continue;
				}
				ArrayList pList = sh.getAnchorPointList();
				SGUtilityForFigureElement.drawAnchorAsFocusedObject( pList, g2d );
			}
		}

	}


	/**
	 * Returns a list of child nodes.
	 * @return a list of chid nodes
	 */
	public ArrayList getChildNodes()
	{
		final ArrayList list = new ArrayList();
		final ArrayList aList = new ArrayList( this.mChildList );
		for( int ii=0; ii<aList.size(); ii++ )
		{
			final ShapeObject el = (ShapeObject)aList.get(ii);
			if( el.isVisible() )
			{
				list.add(el.mElement);
			}
		}

		return list;
	}


	/**
	 * 
	 */
	public String getClassDescription()
	{
		return "Shape";
	}



	// check whether two points are within the radius
	private static boolean isInside(
		final int x1, final int y1, final int x2, final int y2 )
	{
		final int radius = (int)( 1.25f*ANCHOR_SIZE_FOR_FOCUSED_OBJECTS );
		return( ( Math.abs( x1 - x2 ) < radius )
			& ( Math.abs( y1 -  y2 ) < radius ) );
	}



	/**
	 * 
	 */
	protected Set getAvailableChildSet()
	{
		Set set = new HashSet();
		List mList = this.mUndoManager.getMementoList();
		for( int ii=0; ii<mList.size(); ii++ )
		{
			ShapeElementProperties p = (ShapeElementProperties)mList.get(ii);
			set.addAll( p.visibleShapeList );
		}

		return set;		
	}



	/**
	 * 
	 */
	private static class ShapeElementProperties extends SGProperties
	{
		ArrayList visibleShapeList = new ArrayList();


		/**
		 * 
		 *
		 */
		public ShapeElementProperties()
		{
			super();
		}


		public void dispose()
		{
			this.visibleShapeList.clear();
			this.visibleShapeList = null;
		}

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

			ShapeElementProperties p = (ShapeElementProperties)obj;

			if( p.visibleShapeList.equals(this.visibleShapeList) == false ) return false;

			return true;
		}


		/**
		 * 
		 */
		public String toString()
		{
			String str = new String("[");
			str += this.visibleShapeList.toString();
			str += new String("]");

			return str;
		}

		
	}



	/**
	 * An interface of the drawing element.
	 *
	 */
	private static interface IElement
		extends SGINode, SGIPropertyDialogObserver, SGIDisposable
	{
		public void setShape( ShapeObject sh );

		public Object copy();
		
		public void translate( final float dx, final float dy );

		public SGProperties getMement();
		
		public boolean setMement( final SGProperties p );

		public String getName();

		public String getClassDescription();

		public String getInstanceDescription();

		public boolean setAxisValuesWithShape();

		public boolean setShapeWithAxesValues();

		public ArrayList getAnchorPointList();

		public int getMouseLocation( final int x, final int y );
		
		public boolean drag( MouseEvent e, Point pos, final int ml );

		public boolean writeProperty( final Element el );

		public boolean readProperty( final Element element );

	}


	private interface PropertyWithAxes
	{
		public int getXAxisLocation();
		
		public int getYAxisLocation();
	}


	/**
	 * An inner class for the shape objects.
	 * 
	 */
	private class ShapeObject extends SGDrawingElement
		implements SGIUndoable, SGISelectable, SGIMovable, SGICopiable, ActionListener, SGIDisposable
	{

		// the ID number
		private int mID;

		// x axis
		private SGAxis mXAxis;
		
		// y axis
		private SGAxis mYAxis;		

		// the drawing element
		private IElement mElement = null;

		// undo manager
		private SGUndoManager mUndoManager = new SGUndoManager(this);

		protected int mTempXAxis = -1;
		protected int mTempYAxis = -1;

		/**
		 * 
		 */
		private boolean mFrameFlag = false;


		/**
		 * 
		 */
		private SGProperties mTemporaryProperties = null;

		
		/**
		 * Pop-up menu.
		 */
		private JPopupMenu mPopupMenu = new JPopupMenu();


		// The constructor.
		private ShapeObject()
		{
			super();
			this.init();
		}


		// The constructor.
		private ShapeObject( IElement sh )
		{
			super();
			this.setIElement(sh);
			this.init();
		}


		// The constructor.
		private ShapeObject( IElement sh, SGAxis xAxis, SGAxis yAxis )
		{
			super();
			this.setIElement(sh);
			this.setXAxis( xAxis );
			this.setYAxis( yAxis );
			this.init();
		}


		// initialize
		private void init()
		{
			this.createPopupMenu();
		}


		public void finalize()
		{
//			System.out.println("finalize:"+this.mElement);
		}


		/**
		 * 
		 */
		public void dispose()
		{
			super.dispose();

			this.mElement.dispose();
			this.mElement = null;

			this.mPopupMenu = null;
			this.mTemporaryProperties = null;

			this.mUndoManager.dispose();
			this.mUndoManager = null;

			this.mXAxis = null;
			this.mYAxis = null;
		}


		// create the popup menu		
		private boolean createPopupMenu()
		{
			JPopupMenu p = this.mPopupMenu;
			
			p.setBounds( 0, 0, 100, 100 );

			p.add( new JLabel( "  -- Significant Difference --" ) );
			p.addSeparator();

			SGUtility.addItem( p, this, MENUCMD_MOVE_TO_FRONT );
			SGUtility.addItem( p, this, MENUCMD_MOVE_TO_BACK );

			p.addSeparator();

			SGUtility.addItem( p, this, MENUCMD_CUT );
			SGUtility.addItem( p, this, MENUCMD_COPY );
			SGUtility.addItem( p, this, MENUCMD_PASTE );

			p.addSeparator();

			SGUtility.addItem( p, this, MENUCMD_DELETE );
			SGUtility.addItem( p, this, MENUCMD_DUPLICATE );

			p.addSeparator();

			SGUtility.addItem( p, this, MENUCMD_PROPERTY );

			return true;
		}



		/**
		 * 
		 */
		public void actionPerformed( ActionEvent e )
		{
			final String command = e.getActionCommand();
			final Object source = e.getSource();

			if( command.equals( MENUCMD_PROPERTY ) )
			{
				// clear all focused objects other type object clicked
				SGShapeElement.this.clearFocusedObjectOtherThan( this );

				// notify to figure
				SGShapeElement.this.setPropertiesOfSelectedObjects();
			}
			else if(
				command.equals( MENUCMD_COPY )
				| command.equals( MENUCMD_CUT )
				| command.equals( MENUCMD_PASTE )
				| command.equals( MENUCMD_DELETE )
				| command.equals( MENUCMD_DUPLICATE )
				| command.equals( MENUCMD_MOVE_TO_FRONT )
				| command.equals( MENUCMD_MOVE_TO_BACK )
			)
			{
				notifyToListener( command );
			}
		}


		public IElement getIElement()
		{
			return this.mElement;
		}

		public void setIElement( IElement sh )
		{
			this.mElement = sh;
		}

		public void setID( final int id )
		{
			this.mID = id;
		}
		
		public int getID()
		{
			return this.mID;
		}

		private SGShapeElement getShapeElement()
		{
			return SGShapeElement.this;
		}

		private SGIAxisElement getAxisElement()
		{
			return this.getShapeElement().getAxisElement();
		}

		public SGAxis getXAxis()
		{
			return this.mXAxis;
		}
		
		public SGAxis getYAxis()
		{
			return this.mYAxis;
		}
		
		public void setXAxis( SGAxis axis )
		{
			this.mXAxis = axis;
		}

		public void setYAxis( SGAxis axis )
		{
			this.mYAxis = axis;
		}

		private SGDrawingElement getDrawingElement()
		{
			return (SGDrawingElement)this.mElement;
		}

		private SGIDrawingElementJava2D getDrawingElement2D()
		{
			return (SGIDrawingElementJava2D)this.mElement;
		}

		public boolean isVisible()
		{
			return this.getDrawingElement().isVisible();
		}

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

		private void paintElement( Graphics2D g2d )
		{
			if( this.isValid() )
			{
				this.getDrawingElement2D().paintElement(g2d);
			}
		}

		public boolean zoom( final float mag )
		{
			return this.getDrawingElement().zoom(mag);		
		}

		private boolean mValidFlag = true;

		public boolean isValid()
		{
			return this.mValidFlag;
		}

		public void setValid( final boolean b )
		{
			this.mValidFlag = b;
		}


		public boolean setAxisValuesWithShape()
		{
			return this.mElement.setAxisValuesWithShape();
		}

		public boolean setShapeWithAxesValues()
		{
			return this.mElement.setShapeWithAxesValues();
		}


		public boolean contains( final int x, final int y )
		{
			return ((SGDrawingElement)this.mElement).contains(x,y);
		}


		public JPopupMenu getPopupMenu()
		{
			return this.mPopupMenu;
		}


		/**
		 * 
		 * @return
		 */
		private ArrayList getAnchorPointList()
		{
			return this.mElement.getAnchorPointList();
		}



		/**
		 * Location of mouse pointer.
		 */
		private int mMouseLocation;



		/**
		 * マウス位置を属性に設定
		 */
		private int getMouseLocation( final int x, final int y )
		{
			return this.mElement.getMouseLocation(x,y);
		}


		/**
		 * 
		 */
		private Cursor getCursor( final int location )
		{

			Cursor cur = null;

			switch( location )
			{
				case NORTH:
				{
					cur = new Cursor( Cursor.N_RESIZE_CURSOR );
					break;
				}
				case SOUTH:
				{
					cur = new Cursor( Cursor.S_RESIZE_CURSOR );
					break;
				}
				case WEST:
				{
					cur = new Cursor( Cursor.W_RESIZE_CURSOR );
					break;
				}
				case EAST:
				{
					cur = new Cursor( Cursor.E_RESIZE_CURSOR );
					break;
				}
				case NORTH_WEST:
				{
					cur = new Cursor( Cursor.NW_RESIZE_CURSOR );
					break;
				}
				case SOUTH_WEST:
				{
					cur = new Cursor( Cursor.SW_RESIZE_CURSOR );
					break;
				}
				case NORTH_EAST:
				{
					cur = new Cursor( Cursor.NE_RESIZE_CURSOR );
					break;
				}
				case SOUTH_EAST:
				{
					cur = new Cursor( Cursor.SE_RESIZE_CURSOR );
					break;
				}
				default:
				{
					cur = new Cursor( Cursor.HAND_CURSOR );
				}
			}

			return cur;
		}


		// on pressed
		private boolean press( final MouseEvent e )
		{

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

			if( this.contains(x,y) )
			{

				final float mag = this.getMagnification();

//				// set the temporary object
//				this.mTempSymbol = new SigDiffSymbol();
//				mTempSymbol.setMagnification( mag );
//				mTempSymbol.setLocation( this.getX(), this.getY() );
//				mTempSymbol.setSize(
//					this.getWidth(),
//					this.getPerpendicularHeight1(),
//					this.getPerpendicularHeight2()
//				);

				this.mMouseLocation = this.getMouseLocation(x,y);
				Cursor cur = null;
				if( this.mMouseLocation==OTHER )
				{
					cur = Cursor.getPredefinedCursor( Cursor.MOVE_CURSOR );
				}
				else
				{
					this.getCursor( this.mMouseLocation );
				}
				setMouseCursor( cur );

				return true;
			}

			return false;
		}



		// on dragged
		private boolean drag( MouseEvent e )
		{
			if( mPressedPoint == null )
			{
				return false;
			}

			// translation
			if( mMouseLocation == OTHER )
			{
				final boolean flag = this.dragOtherPoint(e);
				return flag;
			}
			else
			{
				// create a temporary object
				Point posNew = new Point( mPressedPoint );

				// drag the element
				this.mElement.drag( e, posNew, mMouseLocation );

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

			return true;
		}


		/**
		 * 
		 */
		private boolean dragOtherPoint( final MouseEvent e )
		{
			// parallel displacement
			if ( mPressedPoint != null )
			{
				// set the location to the symbol
				final int dx = e.getX() - mPressedPoint.x;
				final int dy = e.getY() - mPressedPoint.y;
				this.translate(dx,dy);

				mPressedPoint = e.getPoint();
			}

			return true;
		}




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


		/**
		 * 
		 * @return
		 */
		public int getAxisConfiguration( SGAxis axis )
		{
			return mAxisElement.getConfigurationInPlane( axis );
		}


		/**
		 * 
		 * @param xConfig
		 */
		public void setXAxis( final int xConfig )
		{
			this.setXAxis( mAxisElement.getAxisInPlane( xConfig ) );
		}


		/**
		 * 
		 * @param yConfig
		 */
		public void setYAxis( final int yConfig )
		{
			this.setYAxis( mAxisElement.getAxisInPlane( yConfig ) );
		}


		/**
		 * 
		 */
		public Object copy()
		{
			IElement el = (IElement)this.mElement.copy();
			
			SGAxis xAxis = this.getXAxis();
			SGAxis yAxis = this.getYAxis();
			final int xConfig = this.getAxisConfiguration( this.getXAxis() );
			final int yConfig = this.getAxisConfiguration( this.getYAxis() );

			ShapeObject sh = new ShapeObject(el);
			el.setShape(sh);
			sh.setXAxis( xAxis );
			sh.setYAxis( yAxis );
			sh.mTempXAxis = xConfig;
			sh.mTempYAxis = yConfig;

			return sh;
		}


		/**
		 * 
		 */
		public void translate( final float dx, final float dy )
		{
			this.mElement.translate(dx,dy);
		}


		/**
		 * 
		 */
		public boolean isChanged()
		{
			return this.mChangedFlag;
		}


		private boolean mChangedFlag = false;

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


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


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


		/**
		 * Update the list of history.
		 */
		public boolean updateHistory()
		{
			return this.mUndoManager.updateHistory();
		}


		/**
		 * 
		 */
		public void initUndoBuffer()
		{
			this.mUndoManager.initUndoBuffer();
		}


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

//this.mUndoManager.dump();

			this.setShapeWithAxesValues();

			return true;
		}


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

			this.setShapeWithAxesValues();

			return true;
		}


		/**
		 * Just calls undo method.
		 */
		public boolean undo()
		{
			return this.setMementoBackward();
		}


		/**
		 * Just calls redo method.
		 */
		public boolean redo()
		{
			return this.setMementoForward();
		}


		/**
		 * 
		 */
		public SGProperties getMemento()
		{
			return this.mElement.getMement();
		}


		/**
		 * 
		 */		
		public boolean setMemento( SGProperties p )
		{
			if( ( p instanceof PropertyWithAxes ) == false ) return false;

			if( super.setProperties(p) == false ) return false;

			PropertyWithAxes rp = (PropertyWithAxes)p;

			SGAxis xAxis = mAxisElement.getAxisInPlane( rp.getXAxisLocation() );
			if( xAxis==null ) return false;
			this.setXAxis( xAxis );

			SGAxis yAxis = mAxisElement.getAxisInPlane( rp.getYAxisLocation() );
			if( yAxis==null ) return false;
			this.setYAxis( yAxis );

			return this.mElement.setMement(p);
		}


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

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


		/**
		 * 
		 * @param document
		 * @return
		 */
		public Element createElement( final Document document )
		{
			Element el = document.createElement( this.mElement.getName() );

			if( this.writeProperty(el) == false )
			{
				return null;
			}

			return el;
		}


		/**
		 * 
		 * @param element
		 * @return
		 */
		public boolean writeProperty( final Element element )
		{
			SGIAxisElement aElement = this.getAxisElement();			
			element.setAttribute(
				KEY_X_AXIS_POSITION, aElement.getAxisLocation( this.getXAxis() ) );
			element.setAttribute(
				KEY_Y_AXIS_POSITION, aElement.getAxisLocation( this.getYAxis() ) );

			if( this.mElement.writeProperty(element) == false )
			{
				return false;
			}

			return true;
		}



		/**
		 * 
		 */
		public boolean readProperty( final Element element )
		{
			SGIAxisElement aElement = this.getAxisElement();
			String str = null;

			// create an IElement object
			String tag = element.getTagName();
			IElement ie = null;
			if( tag.equals( Rect.NAME ) )
			{
				ie = new Rect();
			}
			else if( tag.equals( Ellipse.NAME ) )
			{
				ie = new Ellipse();
			}
			else if( tag.equals( Arrow.NAME ) )
			{
				ie = new Arrow();
			}
			else
			{
				return false;
			}
			this.setIElement(ie);
			ie.setShape( this );


			//
			// read axis properties
			//

			// x axis
			str = element.getAttribute( KEY_X_AXIS_POSITION );
			if( str.length()==0 )
			{
				return false;
			}
			SGAxis xAxis = aElement.getAxis(str);
			if( xAxis==null )
			{
				return false;
			}
			
			// y axis
			str = element.getAttribute( KEY_Y_AXIS_POSITION );
			if( str.length()==0 )
			{
				return false;
			}
			SGAxis yAxis = aElement.getAxis(str);
			if( yAxis==null )
			{
				return false;
			}

			this.setXAxis( xAxis );
			this.setYAxis( yAxis );


			// read properties of IElement object
			if( ie.readProperty(element) == false )
			{
				return false;
			}
		
			return true;
		}



	}




	/**
	 * A class of rectangles.
	 *
	 */
	private static class Rect extends SGDrawingElementRectangle2D
		implements IElement, SGIRectangleConstants, SGIRectangularShapeDialogObserver
	{
		public static final String NAME = "Rectangle";
		
		public static final String KEY_LEFT_X_VALUE = "LeftXValue";
		public static final String KEY_RIGHT_X_VALUE = "RightXValue";
		public static final String KEY_BOTTOM_Y_VALUE = "BottomYValue";
		public static final String KEY_TOP_Y_VALUE = "TopYValue";

		
		// Shape object
		protected ShapeObject mShape = null;


		/**
		 * 
		 */
		protected double mXValue1;

		
		/**
		 * 
		 */
		protected double mYValue1;


		/**
		 * 
		 */
		protected double mXValue2;

		
		/**
		 * 
		 */
		protected double mYValue2;


		//		
		private Rect()
		{
			super();
			this.init();
		}


		/**
		 * 
		 */
		private boolean init()
		{
			this.setWidth( SHAPE_RETANGLE_WIDTH );
			this.setHeight( SHAPE_RETANGLE_HEIGHT );
			this.setColor( SHAPE_RECTANGLE_INNER_COLOR );
			this.setEdgeLineWidth( SHAPE_RCTANGLE_EDGE_LINE_WIDTH );
			this.setEdgeLineType( SHAPE_RCTANGLE_EDGE_LINE_TYPE );
			this.setEdgeLineColor( SHAPE_RECTANGLE_EDGE_LINE_COLOR );

//			this.createPopupMenu();

			return true;
		}


//		public boolean zoom( final float mag )
//		{
//			super.zoom(mag);
//			this.setShapeWithAxesValues();
//			return true;
//		}


		public void dispose()
		{
			super.dispose();
			this.mShape = null;
			this.mTemporaryProperties = null;
		}


		/**
		 * 
		 */
		public void setShape( ShapeObject sh )
		{
			this.mShape = sh;
		}


		protected SGShapeElement getShapeElement()
		{
			return this.mShape.getShapeElement();
		}


		protected int getID()
		{
			return this.mShape.getID();
		}

		protected SGAxis getXAxis()
		{
			return this.mShape.getXAxis();
		}

		protected SGAxis getYAxis()
		{
			return this.mShape.getYAxis();
		}


		/**
		 * 
		 */
		public float getLineWidth( final String unit )
		{
			return (float)SGUtilityText.convertFromPoint( this.getLineWidth(), unit );
		}


		/**
		 * 
		 */
		public boolean setLineWidth( final float width, final String unit )
		{
			return this.setLineWidth( (float)SGUtilityText.convertToPoint( width, unit ) );
		}


		/**
		 * 
		 * @return
		 */
		protected boolean isHorizontallyReversed()
		{
			return ( this.getWidth() < 0.0f );
		}


		/**
		 * 
		 * @return
		 */
		protected boolean isPerpendicularlyReversed()
		{
			return ( this.getHeight() < 0.0f );
		}


		/**
		 * 
		 * @return
		 */
		public float getX1()
		{
			return this.getShapeElement().getXFromGraphRectValue( super.getX() );
		}


		/**
		 * 
		 */
		public float getX()
		{
			return this.getX1();
		}


		/**
		 * 
		 * @return
		 */
		public float getX2()
		{
			return this.getX1() + this.getMagnification()*this.getWidth();
		}


		/**
		 * 
		 */
		public float getY1()
		{
			return this.getShapeElement().getYFromGraphRectValue( super.getY() );
		}

		/**
		 * 
		 */
		public float getY()
		{
			return this.getY1();
		}


		/**
		 * 
		 * @return
		 */
		public float getY2()
		{
			return this.getY1() + this.getMagnification()*this.getHeight();
		}


		/**
		 * 
		 * @return
		 */
		public float getLeftX()
		{
			return !this.isHorizontallyReversed() ? this.getX1() : this.getX2();
		}


		/**
		 * 
		 * @return
		 */
		public float getRightX()
		{
			return !this.isHorizontallyReversed() ? this.getX2() : this.getX1();
		}


		/**
		 * 
		 * @return
		 */
		public float getCenterX()
		{
			return this.getX1() + 0.50f*this.getMagnification()*this.getWidth();
		}


		/**
		 * 
		 * @return
		 */
		public float getTopY()
		{
			return !this.isPerpendicularlyReversed() ? this.getY1() : this.getY2();
		}


		/**
		 * 
		 * @return
		 */
		public float getBottomY()
		{
			return !this.isPerpendicularlyReversed() ? this.getY2() : this.getY1();
		}


		/**
		 * 
		 * @return
		 */
		public float getCenterY()
		{
			return this.getY1() + 0.50f*this.getMagnification()*this.getHeight();
		}


		/**
		 * 
		 * @param value
		 */
		public void setLeftX( final float x )
		{
			if( !this.isHorizontallyReversed() )
			{
				this.setX1(x);
			}
			else
			{
				this.setX2(x);
			}
		}


		/**
		 * 
		 * @param value
		 */
		public void setRightX( final float x )
		{
			if( !this.isHorizontallyReversed() )
			{
				this.setX2(x);
			}
			else
			{
				this.setX1(x);
			}
		}


		/**
		 * 
		 * @param value
		 */
		public void setTopY( final float y )
		{
			if( !this.isPerpendicularlyReversed() )
			{
				this.setY1(y);
			}
			else
			{
				this.setY2(y);
			}
		}


		/**
		 * 
		 * @param value
		 */
		public void setBottomY( final float y )
		{
			if( !this.isPerpendicularlyReversed() )
			{
				this.setY2(y);
			}
			else
			{
				this.setY1(y);
			}
		}


		/**
		 * 
		 * @param x
		 * @return
		 */
		public boolean setX1( final float x )
		{
			return this.setX(x);
		}


		/**
		 * 
		 * @param x
		 * @return
		 */
		public boolean setX2( final float x )
		{
			return this.setX( x - this.getMagnification()*this.getWidth() );
		}


		/**
		 * 
		 * @param y
		 * @return
		 */
		public boolean setY1( final float y )
		{
			return this.setY(y);
		}


		/**
		 * 
		 * @param y
		 * @return
		 */
		public boolean setY2( final float y )
		{
			return this.setY( y - this.getMagnification()*this.getHeight() );
		}


		/**
		 * 
		 */
		public boolean setX( final float x )
		{
			return super.setX( this.getShapeElement().getGraphRectValueX(x) );
		}


		/**
		 * 
		 */
		public boolean setY( final float y )
		{
			return super.setY( this.getShapeElement().getGraphRectValueY(y) );
		}


		/**
		 * 
		 */
		public boolean setShapeWithAxesValues()
		{
			ShapeObject sh = this.mShape;
			SGAxis xAxis = this.getXAxis();
			SGAxis yAxis = this.getYAxis();
			
			SGShapeElement sElement = this.getShapeElement();

			final float x1 = sElement.calcLocation( this.mXValue1, xAxis, true );
			final float y1 = sElement.calcLocation( this.mYValue1, yAxis, false);
			final float x2 = sElement.calcLocation( this.mXValue2, xAxis, true );
			final float y2 = sElement.calcLocation( this.mYValue2, yAxis, false);

			if( Float.isNaN(x1) | Float.isNaN(y1) | Float.isNaN(x2) | Float.isNaN(y2) )
			{
				sh.setValid( false );
				return false;
			}
			else
			{
				sh.setValid( true );
			}

//final float ratio = SGIConstants.CM_POINT_RATIO;
//System.out.println(x1*ratio);
//System.out.println(x2*ratio);
//System.out.println(y1*ratio);
//System.out.println(y2*ratio);
//System.out.println();

			final float mag = this.getMagnification();
			final float w = (x2-x1)/mag;
			final float h = (y2-y1)/mag;
			this.setLocation(x1,y1);
			this.setWidth(w);
			this.setHeight(h);

			return true;
		}


		/**
		 * 
		 */
		public boolean setAxisValuesWithShape()
		{
			SGAxis xAxis = this.getXAxis();
			SGAxis yAxis = this.getYAxis();

			SGShapeElement sElement = this.getShapeElement();

			final double xValue1 = sElement.calcValue( this.getX1(), xAxis, true );
			final double yValue1 = sElement.calcValue( this.getY1(), yAxis, false );
			final double xValue2 = sElement.calcValue( this.getX2(), xAxis, true );
			final double yValue2 = sElement.calcValue( this.getY2(), yAxis, false );

			this.mXValue1 = sElement.getNumberInRangeOrder( xValue1, xAxis );
			this.mYValue1 = sElement.getNumberInRangeOrder( yValue1, yAxis );
			this.mXValue2 = sElement.getNumberInRangeOrder( xValue2, xAxis );
			this.mYValue2 = sElement.getNumberInRangeOrder( yValue2, yAxis );

			return true;
		}



		/**
		 * マウス位置を属性に設定
		 */
		public int getMouseLocation( final int x, final int y )
		{
			final int left = (int)this.getLeftX();
			final int right = (int)this.getRightX();
			final int top = (int)this.getTopY();
			final int bottom = (int)this.getBottomY();
			final int centerX = (int)this.getCenterX();
			final int centerY = (int)this.getCenterY();

			int location = -1;
			if( isInside( left, top, x, y ) )
			{
				location = NORTH_WEST;
			}
			else if( isInside( left, centerY, x, y ) )
			{
				location = WEST;
			}
			else if( isInside( left, bottom, x, y ) )
			{
				location = SOUTH_WEST;
			}
			else if( isInside( right, top, x, y ) )
			{
				location = NORTH_EAST;
			}
			else if( isInside( right, centerY, x, y ) )
			{
				location = EAST;
			}
			else if( isInside( right, bottom, x, y ) )
			{
				location = SOUTH_EAST;
			}
			else if( isInside( centerX, top, x, y ) )
			{
				location = NORTH;
			}
			else if( isInside( centerX, bottom, x, y ) )
			{
				location = SOUTH;
			}
			else if( this.getElementBounds().contains(x,y) )
			{
				location = OTHER;
			}

			return location;
		}


		/**
		 * 
		 * @return
		 */
		public ArrayList getAnchorPointList()
		{
			ArrayList list = new ArrayList();

			final float mag = this.getMagnification();
			final float x = this.getX();
			final float y = this.getY();
			final float w = mag*this.getWidth();
			final float h = mag*this.getHeight();
			final float cx = x + 0.50f*w;
			final float cy = y + 0.50f*h;

			Point2D pNW = new Point2D.Float(x,y);
			Point2D pNE = new Point2D.Float(x+w,y);
			Point2D pSW = new Point2D.Float(x,y+h);
			Point2D pSE = new Point2D.Float(x+w,y+h);
			Point2D pW = new Point2D.Float(x,cy);
			Point2D pE = new Point2D.Float(x+w,cy);
			Point2D pN = new Point2D.Float(cx,y);
			Point2D pS = new Point2D.Float(cx,y+h);

			list.add(pNW);
			list.add(pNE);
			list.add(pSW);
			list.add(pSE);
			list.add(pW);
			list.add(pE);
			list.add(pN);
			list.add(pS);

			return list;
		}


		/**
		 * 
		 */
		public boolean drag(
			MouseEvent e, Point pos, final int ml )
		{
			Rectangle2D rect = this.getElementBounds();
//System.out.println(rect.getBounds());

			// update the rectangle
			SGUtility.resizeRectangle( rect, pos, e, ml );
//System.out.println(rect.getBounds());

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

			this.setWidth(w);
			this.setHeight(h);
			this.setLeftX(x);
			this.setTopY(y);

//			this.setRightX(x+w);
//			this.setBottomY(y+h);

//System.out.println(this.getElementBounds().getBounds());
//System.out.println();

			return true;
		}


		/**
		 * 
		 */
		public Object copy()
		{
			Rect el = new Rect();
			el.setShape( this.mShape );
			el.setMagnification( this.getMagnification() );
			el.setProperties( this.getProperties() );
			el.setShapeWithAxesValues();

			return el;
		}


		/**
		 * 
		 */
		public void translate( final float dx, final float dy )
		{
			this.setLocation( this.getX() + dx, this.getY() + dy );
			this.setAxisValuesWithShape();
		}


		/**
		 * Overrode for the anchors.
		 */
		public boolean contains( final int x, final int y )
		{
			if( super.contains(x,y) == true )
			{
				return true;
			}

			// if anchor is displayed
			if( this.mShape.isSelected() )
			{
				final int left = (int)this.getLeftX();
				final int right = (int)this.getRightX();
				final int top = (int)this.getTopY();
				final int bottom = (int)this.getBottomY();
				final int centerX = (int)this.getCenterX();
				final int centerY = (int)this.getCenterY();

				final boolean b
					= isInside( left, top, x, y )
					| ( isInside( left, centerY, x, y ) )
					| ( isInside( left, bottom, x, y ) )
					| ( isInside( right, top, x, y ) )
					| ( isInside( right, centerY, x, y ) )
					| ( isInside( right, bottom, x, y ) )
					| ( isInside( centerX, top, x, y ) )
					| ( isInside( centerX, bottom, x, y ) );
				if(b)
				{
					return true;
				}
			}

			return false;
		}



		/**
		 * 
		 */
		public String getName()
		{
			return NAME;
		}


		/**
		 * 
		 */
		public String getClassDescription()
		{
			return "";
		}


		/**
		 * 
		 * @return
		 */
		public String getInstanceDescription()
		{
			SGIAxisElement aElement = this.getShapeElement().getAxisElement();
			String xAxis = aElement.getAxisLocation( this.getXAxis() );
			String yAxis = aElement.getAxisLocation( this.getYAxis() );

			String str = this.getID() + ": " + this.getName() + ", ";
			str += xAxis + ", " + yAxis + ", ";
			str += "( X=" + this.mXValue1 + ", Y=" + this.mYValue1 + " )";

			return str;
		}


		/**
		 * 
		 */
		public ArrayList getChildNodes()
		{
			return new ArrayList();
		}


		/**
		 * 
		 */
		public SGProperties getProperties()
		{
			SGProperties p = new RectangularShapeProperties();
			if( this.getProperties(p) == false ) return null;
			return p;
		}


		/**
		 * 
		 */
		public boolean getProperties( SGProperties p )
		{
			if( ( p instanceof RectangularShapeProperties) == false )
			{
				return false;
			}
			
			if( super.getProperties(p) == false ) return false;

			RectangularShapeProperties rp = (RectangularShapeProperties)p;
			
			SGShapeElement sElement = this.getShapeElement();
			SGIAxisElement aElement = sElement.getAxisElement();

			SGAxis xAxis = this.getXAxis();
			SGAxis yAxis = this.getYAxis();
			rp.setXAxisLocation( aElement.getConfigurationInPlane(xAxis) );
			rp.setYAxisLocation( aElement.getConfigurationInPlane(yAxis) );

			rp.setXValue1( this.mXValue1 );
			rp.setYValue1( this.mYValue1 );
			rp.setXValue2( this.mXValue2 );
			rp.setYValue2( this.mYValue2 );

//System.out.println(this.mXValue1+"  "+this.mXValue2);
			return true;
		}


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

			if( super.setProperties(p) == false ) return false;

			RectangularShapeProperties rp = (RectangularShapeProperties)p;

			final Double x1 = rp.getXValue1();
			if( x1==null ) return false;
			this.mXValue1 = x1.doubleValue();
			
			final Double y1 = rp.getYValue1();
			if( y1==null ) return false;
			this.mYValue1 = y1.doubleValue();

			final Double x2 = rp.getXValue2();
			if( x2==null ) return false;
			this.mXValue2 = x2.doubleValue();
			
			final Double y2 = rp.getYValue2();
			if( y2==null ) return false;
			this.mYValue2 = y2.doubleValue();
			
			return true;
		}


		/**
		 * 
		 */
		public SGProperties getMement()
		{
			return this.getProperties();
		}
		
		/**
		 * 
		 */
		public boolean setMement( SGProperties p )
		{
			return this.setProperties(p);
		}



//
// dialog
//

		/**
		 * 
		 */
		public double getLeftXValue()
		{
			return !this.isHorizontallyReversed() ? this.mXValue1 : this.mXValue2;
		}


		/**
		 * 
		 */
		public double getTopYValue()
		{
			return !this.isPerpendicularlyReversed() ? this.mYValue1 : this.mYValue2;
		}


		/**
		 * 
		 */
		public double getRightXValue()
		{
			return !this.isHorizontallyReversed() ? this.mXValue2 : this.mXValue1;
		}


		/**
		 * 
		 */
		public double getBottomYValue()
		{
			return !this.isPerpendicularlyReversed() ? this.mYValue2 : this.mYValue1;
		}


		/**
		 * 
		 */
		public float getLineWidth()
		{
			return this.getEdgeLineWidth();
		}

		/**
		 * 
		 */
		public int getLineType()
		{
			return this.getEdgeLineType();
		}

		/**
		 * 
		 */
		public Color getInnerColor()
		{
			return this.getColor(0);
		}


		/**
		 * 
		 */
		public Color getLineColor()
		{
			return this.getEdgeLineColor();
		}


		/**
		 * 
		 */
		public void setLeftXValue( final double value )
		{
			if( !this.isHorizontallyReversed() )
			{
				this.mXValue1 = value;
			}
			else
			{
				this.mXValue2 = value;
			}
		}


		/**
		 * 
		 */
		public void setTopYValue( final double value )
		{
			if( !this.isPerpendicularlyReversed() )
			{
				this.mYValue1 = value;
			}
			else
			{
				this.mYValue2 = value;
			}
		}


		/**
		 * 
		 */
		public void setRightXValue( final double value )
		{
			if( !this.isHorizontallyReversed() )
			{
				this.mXValue2 = value;
			}
			else
			{
				this.mXValue1 = value;
			}
		}


		/**
		 * 
		 */
		public void setBottomYValue( final double value )
		{
			if( !this.isPerpendicularlyReversed() )
			{
				this.mYValue2 = value;
			}
			else
			{
				this.mYValue1 = value;
			}
		}


		/**
		 * 
		 */
		public boolean setLineWidth( final float width )
		{
			return this.setEdgeLineWidth(width);
		}


		/**
		 * 
		 */
		public boolean setLineType( final int type )
		{
			return this.setEdgeLineType( type );
		}


		/**
		 * 
		 */
		public boolean setInnerColor( final Color cl )
		{
			return this.setColor(cl);
		}


		/**
		 * 
		 */
		public boolean setLineColor( final Color cl )
		{
			return this.setEdgeLineColor(cl);
		}


		/**
		 * 
		 */
		public int getXAxisConfiguration()
		{
			return this.mShape.getAxisConfiguration( this.getXAxis() );
		}


		/**
		 * 
		 */
		public int getYAxisConfiguration()
		{
			return this.mShape.getAxisConfiguration( this.getYAxis() );
		}
		
		
		/**
		 * 
		 */
		public void setXAxisConfiguration( final int config )
		{
			this.mShape.setXAxis( config );
		}


		/**
		 * 
		 */
		public void setYAxisConfiguration( final int config )
		{
			this.mShape.setYAxis( config );
		}



		/**
		 * 
		 * @param config
		 * @param value
		 * @return
		 */
		public boolean hasValidLeftXValue( final int config, final Number value )
		{
			SGIAxisElement aElement = this.getShapeElement().getAxisElement();
			final SGAxis axis = (config==-1) ? this.getXAxis() : aElement.getAxisInPlane( config );
			final double v = (value!=null) ? value.doubleValue() : this.getLeftXValue();
			return axis.isValidValue(v);
		}


		/**
		 * 
		 * @param config
		 * @param value
		 * @return
		 */
		public boolean hasValidTopYValue( final int config, final Number value )
		{
			SGIAxisElement aElement = this.getShapeElement().getAxisElement();
			final SGAxis axis = (config==-1) ? this.mShape.getYAxis() : aElement.getAxisInPlane( config );
			final double v = (value!=null) ? value.doubleValue() : this.getTopYValue();
			return axis.isValidValue(v);
		}


		/**
		 * 
		 * @param config
		 * @param value
		 * @return
		 */
		public boolean hasValidRightXValue( final int config, final Number value )
		{
			SGIAxisElement aElement = this.getShapeElement().getAxisElement();
			final SGAxis axis = (config==-1) ? this.getXAxis() : aElement.getAxisInPlane( config );
			final double v = (value!=null) ? value.doubleValue() : this.getRightXValue();
			return axis.isValidValue(v);
		}


		/**
		 * 
		 * @param config
		 * @param value
		 * @return
		 */
		public boolean hasValidBottomYValue( final int config, final Number value )
		{
			SGIAxisElement aElement = this.getShapeElement().getAxisElement();
			final SGAxis axis = (config==-1) ? this.getYAxis() : aElement.getAxisInPlane( config );
			final double v = (value!=null) ? value.doubleValue() : this.getBottomYValue();
			return axis.isValidValue(v);
		}


		/**
		 * 
		 */
		private SGProperties mTemporaryProperties = null;


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


		/**
		 * 
		 */
		public boolean commit()
		{
			// ダイアログを出す前後でプロパティが変更されている場合のみ、
			// 履歴を更新する

			SGProperties pTemp = this.mTemporaryProperties;
			SGProperties pPresent = this.getProperties();
			if( pTemp.equals(pPresent) == false )
			{
				this.mShape.setChanged(true);
			}

			this.setShapeWithAxesValues();

			SGShapeElement sh = this.getShapeElement();
			sh.repaint();
			sh.notifyChange();

			return true;
		}


		/**
		 * 
		 */
		public boolean cancel()
		{
			if( this.setProperties( this.mTemporaryProperties ) == false )
			{
				return false;
			}
			this.mTemporaryProperties = null;

			this.setShapeWithAxesValues();

			SGShapeElement sh = this.getShapeElement();
			sh.repaint();
			sh.notifyChange();

			return true;
		}


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

			SGShapeElement sh = this.getShapeElement();
			sh.repaint();
			sh.notifyChange();

//this.dump();

			return true;
		}



		/**
		 * Returns a property dialog.
		 * @return property dialog
		 */
		public SGPropertyDialog getPropertyDialog()
		{
			SGShapeElement sh = this.getShapeElement();
			return sh.mRectangularShapeDialog;
		}



		public void dump()
		{
			System.out.println(this.mXValue1+"  "+this.mXValue2);
			System.out.println(this.mYValue1+"  "+this.mYValue2);
			System.out.println();
		}



		//
		// property file
		//

		/**
		 * 
		 */
		public boolean writeProperty( final Element el )
		{
			String cm = SGUtilityNumber.cm;
			String pt = SGUtilityNumber.pt;
			String degree = SGUtilityNumber.degree;
			final float ratio = SGIConstants.CM_POINT_RATIO;

			el.setAttribute( KEY_LEFT_X_VALUE, Double.toString( this.getLeftXValue() ) );
			el.setAttribute( KEY_RIGHT_X_VALUE, Double.toString( this.getRightXValue() ) );
			el.setAttribute( KEY_BOTTOM_Y_VALUE, Double.toString( this.getBottomYValue() ) );
			el.setAttribute( KEY_TOP_Y_VALUE, Double.toString( this.getTopYValue() ) );

			el.setAttribute( KEY_EDGE_LINE_WIDTH, Float.toString( this.mEdgeLineWidth ) + pt );
			el.setAttribute( KEY_EDGE_LINE_TYPE, SGDrawingElementLine.getLineTypeName( this.getEdgeLineType() ) );
			el.setAttribute( KEY_BACKGROUND_TRANSPARENT, Boolean.toString( this.isTransparent() ) );
			el.setAttribute( KEY_INNER_COLOR, SGUtilityText.getColorListString( this.mColorList ) );
			el.setAttribute( KEY_EDGE_LINE_COLOR, SGUtilityText.getColorString( this.mEdgeLineColor ) );

			return true;
		}


		/**
		 * 
		 */
		public boolean readProperty( final Element el )
		{
			final float ratio = SGIConstants.CM_POINT_RATIO;

			String str = null;
			Number num = null;
			Color cl = null;
			Boolean b = null;
			ArrayList list = null;

			final SGAxis xAxis = this.getXAxis();
			final SGAxis yAxis = this.getYAxis();


			// left x value
			str = el.getAttribute( KEY_LEFT_X_VALUE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getDouble(str);
			if( num==null )
			{
				return false;
			}
			final double leftXValue = num.doubleValue();
			if( xAxis.isValidValue( leftXValue ) == false )
			{
				return false;
			}

			// right x value
			str = el.getAttribute( KEY_RIGHT_X_VALUE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getDouble(str);
			if( num==null )
			{
				return false;
			}
			final double rightXValue = num.doubleValue();
			if( xAxis.isValidValue( rightXValue ) == false )
			{
				return false;
			}

			// bottom y value
			str = el.getAttribute( KEY_BOTTOM_Y_VALUE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getDouble(str);
			if( num==null )
			{
				return false;
			}
			final double bottomYValue = num.doubleValue();
			if( yAxis.isValidValue( bottomYValue ) == false )
			{
				return false;
			}
			
			// top y value
			str = el.getAttribute( KEY_TOP_Y_VALUE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getDouble(str);
			if( num==null )
			{
				return false;
			}
			final double topYValue = num.doubleValue();
			if( yAxis.isValidValue( topYValue ) == false )
			{
				return false;
			}


			// line width
			str = el.getAttribute( KEY_EDGE_LINE_WIDTH );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getLengthInPoint( str );
			if( num==null )
			{
				return false;
			}
			final float edgeLineWidth = num.floatValue();


			// line type
			str = el.getAttribute( KEY_EDGE_LINE_TYPE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGDrawingElementLine.getLineTypeFromName(str);
			if( num==null )
			{
				return false;
			}
			final int edgeLineType = num.intValue();


			// transparent
			str = el.getAttribute( KEY_BACKGROUND_TRANSPARENT );
			if( str.length()==0 )
			{
				return false;
			}
			b = SGUtilityText.getBoolean(str);
			if( b==null )
			{
				return false;
			}
			final boolean transparent = b.booleanValue();


			// inner color
			str = el.getAttribute( KEY_INNER_COLOR );
			if( str.length()==0 )
			{
				return false;
			}
			list = SGUtilityText.getColorList(str);
			if( list==null )
			{
				return false;
			}
			final ArrayList innerColorList = list;
		

			// edge line color
			str = el.getAttribute( KEY_EDGE_LINE_COLOR );
			if( str.length()==0 )
			{
				return false;
			}
			cl = SGUtilityText.getColorFromString(str);
			if( cl==null )
			{
				return false;
			}
			final Color edgeLineColor = cl;


			// set properties
			this.setLeftXValue( leftXValue );
			this.setRightXValue( rightXValue );
			this.setBottomYValue( bottomYValue );
			this.setTopYValue( topYValue );
			this.setEdgeLineWidth( edgeLineWidth );
			this.setEdgeLineType( edgeLineType );
			this.setTransparent( transparent );
			this.setColor( innerColorList );
			this.setEdgeLineColor( edgeLineColor );
	
			return true;
		}


	}





	/**
	 * A class of ellipses.
	 *
	 */
	private static class Ellipse extends Rect
	{
		public static final String NAME = "Ellipse";

		//
		private Ellipse()
		{
			super();
		}


		/**
		 * 
		 * @return
		 */
		protected Shape getShape()
		{
			Rectangle2D rect = this.getElementBounds();
			Ellipse2D.Float el = new Ellipse2D.Float(
				(float)rect.getX(),
				(float)rect.getY(),
				(float)rect.getWidth(),
				(float)rect.getHeight() );
			return el;
		}


		/**
		 * 
		 */
		public Object copy()
		{
			Ellipse el = new Ellipse();
			el.setShape( this.mShape );
			el.setMagnification( this.getMagnification() );
			el.setProperties( this.getProperties() );
			el.setShapeWithAxesValues();

			return el;
		}


		/**
		 * 
		 */
		public String getName()
		{
			return NAME;
		}

	}



	/**
	 * Properties of rectangular shape.
	 */
	private static class RectangularShapeProperties
		extends RectangleProperties implements PropertyWithAxes
	{

		private double mXValue1 = 0.0;
		private double mYValue1 = 0.0;
		private double mXValue2 = 0.0;
		private double mYValue2 = 0.0;
		private int mXAxisLocation = -1;
		private int mYAxisLocation = -1;

		/**
		 * 
		 */
		public RectangularShapeProperties()
		{
			super();
		}


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

			if( super.equals(obj) == false ) return false;

			RectangularShapeProperties p = (RectangularShapeProperties)obj;

			if( p.mXValue1!=this.mXValue1 ) return false;
			if( p.mYValue1!=this.mYValue1 ) return false;
			if( p.mXValue2!=this.mXValue2 ) return false;
			if( p.mYValue2!=this.mYValue2 ) return false;
			if( this.mXAxisLocation!=p.mXAxisLocation ) return false;
			if( this.mYAxisLocation!=p.mYAxisLocation ) return false;

			return true;
		}

		public Double getXValue1()
		{
			return new Double( this.mXValue1 );
		}
		
		public Double getYValue1()
		{
			return new Double( this.mYValue1 );
		}

		public Double getXValue2()
		{
			return new Double( this.mXValue2 );
		}
		
		public Double getYValue2()
		{
			return new Double( this.mYValue2 );
		}

		public int getXAxisLocation()
		{
			return this.mXAxisLocation;
		}

		public int getYAxisLocation()
		{
			return this.mYAxisLocation;
		}

		public boolean setXValue1( final double value )
		{
			this.mXValue1 = value;
			return true;
		}

		public boolean setYValue1( final double value )
		{
			this.mYValue1 = value;
			return true;
		}

		public boolean setXValue2( final double value )
		{
			this.mXValue2 = value;
			return true;
		}

		public boolean setYValue2( final double value )
		{
			this.mYValue2 = value;
			return true;
		}

		private boolean setXAxisLocation( final int location )
		{
			this.mXAxisLocation = location;
			return true;
		}

		private boolean setYAxisLocation( final int location )
		{
			this.mYAxisLocation = location;
			return true;
		}

	}



	/**
	 * A class of arrow.
	 * 
	 */
	private static class Arrow extends SGDrawingElementArrow2D
		implements IElement, SGIArrowConstants, SGIArrowDialogObserver
	{
		public static final String NAME = "Arrow";

		public static final String KEY_START_X_VALUE = "StartXValue";
		public static final String KEY_START_Y_VALUE = "StartYValue";
		public static final String KEY_END_X_VALUE = "EndXValue";
		public static final String KEY_END_Y_VALUE = "EndYValue";

		/**
		 * 
		 */
		protected float mStartX;
		protected float mStartY;
		protected float mEndX;
		protected float mEndY;

		/**
		 * 
		 */
		protected double mStartXValue;
		protected double mStartYValue;
		protected double mEndXValue;
		protected double mEndYValue;

		protected ShapeObject mShape = null;

		private Arrow()
		{
			super();
			this.init();
		}

		/**
		 * 
		 */
		private boolean init()
		{
			this.setLineWidth( SHAPE_ARROW_LINE_WIDTH );
			this.setLineType( SHAPE_ARROW_LINE_TYPE );
			this.setColor( SHAPE_ARROW_COLOR );
			
			this.setHeadSize( SHAPE_ARROW_HEAD_SIZE );
			this.setHeadOpenAngle( SHAPE_ARROW_HEAD_OPEN_ANGLE );
			this.setHeadCloseAngle( SHAPE_ARROW_HEAD_CLOSE_ANGLE );

//			this.createPopupMenu();

			return true;
		}


		public void dispose()
		{
			super.dispose();
			this.mShape = null;
			this.mTemporaryProperties = null;
		}

		protected SGShapeElement getShapeElement()
		{
			return this.mShape.getShapeElement();
		}


		/**
		 * 
		 */
		public void setShape( ShapeObject sh )
		{
			this.mShape = sh;
		}


		protected int getID()
		{
			return this.mShape.getID();
		}

		protected SGAxis getXAxis()
		{
			return this.mShape.getXAxis();
		}

		protected SGAxis getYAxis()
		{
			return this.mShape.getYAxis();
		}


		private static final int START = NORTH_EAST;
		private static final int END = SOUTH_EAST;
		private static final int BODY = OTHER;


		/**
		 * マウス位置を属性に設定
		 */
		public int getMouseLocation( final int x, final int y )
		{
			final int radius = (int)( 1.25f*ANCHOR_SIZE_FOR_FOCUSED_OBJECTS );

			final int startX = (int)this.getStartX();
			final int startY = (int)this.getStartY();
			final int endX = (int)this.getEndX();
			final int endY = (int)this.getEndY();

			int location = -1;
			{
				if( isInside( startX, startY, x, y ) )
				{
					location = START;
				}
				else if( isInside( endX, endY, x, y ) )
				{
					location = END;
				}
				else
				{
					location = BODY;
				}
			}

			return location;
		}



		/**
		 * 
		 */
		public int getXAxisConfiguration()
		{
			return this.mShape.getAxisConfiguration( this.getXAxis() );
		}


		/**
		 * 
		 */
		public int getYAxisConfiguration()
		{
			return this.mShape.getAxisConfiguration( this.getYAxis() );
		}


		/**
		 * 
		 * @return
		 */
		public float getStartX()
		{
			return this.getShapeElement().getXFromGraphRectValue( this.mStartX );
		}


		/**
		 * 
		 * @return
		 */
		public float getStartY()
		{
			return this.getShapeElement().getYFromGraphRectValue( this.mStartY );
		}


		/**
		 * 
		 * @return
		 */
		public float getEndX()
		{
			return this.getShapeElement().getXFromGraphRectValue( this.mEndX );
		}


		/**
		 * 
		 * @return
		 */
		public float getEndY()
		{
			return this.getShapeElement().getYFromGraphRectValue( this.mEndY );
		}


		/**
		 * 
		 */
		public Color getColor()
		{
			return this.getColor(0);
		}


		/**
		 * 
		 */
		public float getLineWidth( final String unit )
		{
			return (float)SGUtilityText.convertFromPoint( this.getLineWidth(), unit );
		}


		/**
		 * 
		 */
		public float getHeadSize( final String unit )
		{
			return (float)SGUtilityText.convertFromPoint( this.getHeadSize(), unit );
		}


		/**
		 * 
		 */
		public boolean setLineWidth( final float width, final String unit )
		{
			return this.setLineWidth( (float)SGUtilityText.convertToPoint( width, unit ) );
		}


		/**
		 * 
		 */
		public boolean setHeadSize( final float size, final String unit )
		{
			return this.setHeadSize( (float)SGUtilityText.convertToPoint( size, unit ) );
		}


		/**
		 * 
		 */
		public void setXAxisConfiguration( final int config )
		{
			this.mShape.setXAxis( config );
		}


		/**
		 * 
		 */
		public void setYAxisConfiguration( final int config )
		{
			this.mShape.setYAxis( config );
		}


		/**
		 * 
		 */
		public boolean setStartX( final float x )
		{
			super.setStartX(x);
			this.mStartX = this.getShapeElement().getGraphRectValueX(x);
			return true;
		}

		/**
		 * 
		 */
		public boolean setStartY( final float y )
		{
			super.setStartY(y);
			this.mStartY = this.getShapeElement().getGraphRectValueY(y);
			return true;
		}


		/**
		 * 
		 */
		public boolean setEndX( final float x )
		{
			super.setEndX(x);
			this.mEndX = this.getShapeElement().getGraphRectValueX(x);
			return true;
		}

		/**
		 * 
		 */
		public boolean setEndY( final float y )
		{
			super.setEndY(y);
			this.mEndY = this.getShapeElement().getGraphRectValueY(y);
			return true;
		}


		/**
		 * 
		 */
		public double getStartXValue()
		{
			return this.mStartXValue;
		}


		/**
		 * 
		 */
		public double getStartYValue()
		{
			return this.mStartYValue;
		}


		/**
		 * 
		 */
		public double getEndXValue()
		{
			return this.mEndXValue;
		}


		/**
		 * 
		 */
		public double getEndYValue()
		{
			return this.mEndYValue;
		}


		/**
		 * 
		 */
		public boolean setStartXValue( final double value )
		{
			this.mStartXValue = value;
			return true;
		}


		/**
		 * 
		 */
		public boolean setStartYValue( final double value )
		{
			this.mStartYValue = value;
			return true;
		}


		/**
		 * 
		 */
		public boolean setEndXValue( final double value )
		{
			this.mEndXValue = value;
			return true;
		}


		/**
		 * 
		 */
		public boolean setEndYValue( final double value )
		{
			this.mEndYValue = value;
			return true;
		}


		/**
		 * 
		 * @param config
		 * @param value
		 * @return
		 */
		public boolean hasValidStartXValue( final int config, final Number value )
		{
			SGIAxisElement aElement = this.getShapeElement().getAxisElement();
			final SGAxis axis = (config==-1) ? this.getXAxis() : aElement.getAxisInPlane( config );
			final double v = (value!=null) ? value.doubleValue() : this.getStartXValue();
			return axis.isValidValue(v);
		}


		/**
		 * 
		 * @param config
		 * @param value
		 * @return
		 */
		public boolean hasValidStartYValue( final int config, final Number value )
		{
			SGIAxisElement aElement = this.getShapeElement().getAxisElement();
			final SGAxis axis = (config==-1) ? this.getYAxis() : aElement.getAxisInPlane( config );
			final double v = (value!=null) ? value.doubleValue() : this.getStartYValue();
			return axis.isValidValue(v);
		}


		/**
		 * 
		 * @param config
		 * @param value
		 * @return
		 */
		public boolean hasValidEndXValue( final int config, final Number value )
		{
			SGIAxisElement aElement = this.getShapeElement().getAxisElement();
			final SGAxis axis = (config==-1) ? this.getXAxis() : aElement.getAxisInPlane( config );
			final double v = (value!=null) ? value.doubleValue() : this.getEndXValue();
			return axis.isValidValue(v);
		}


		/**
		 * 
		 * @param config
		 * @param value
		 * @return
		 */
		public boolean hasValidEndYValue( final int config, final Number value )
		{
			SGIAxisElement aElement = this.getShapeElement().getAxisElement();
			final SGAxis axis = (config==-1) ? this.mShape.getYAxis() : aElement.getAxisInPlane( config );
			final double v = (value!=null) ? value.doubleValue() : this.getEndYValue();
			return axis.isValidValue(v);
		}


		/**
		 * 
		 * @param open
		 * @param close
		 * @return
		 */
		public boolean hasValidAngle( final Number open, final Number close )
		{
			final float openAngle = (open!=null) ? open.floatValue() : this.getHeadOpenAngle();
			final float closeAngle = (close!=null) ? close.floatValue() : this.getHeadCloseAngle();
			return ( openAngle < closeAngle );
		}


		/**
		 * 
		 */
		public boolean setAxisValuesWithShape()
		{
			SGAxis xAxis = this.getXAxis();
			SGAxis yAxis = this.getYAxis();

			SGShapeElement sElement = this.getShapeElement();

//System.out.println(super.getStartX()+"  "+this.getStartX());

			final double xValue1 = sElement.calcValue( this.getStartX(), xAxis, true );
			final double yValue1 = sElement.calcValue( this.getStartY(), yAxis, false );
			final double xValue2 = sElement.calcValue( this.getEndX(), xAxis, true );
			final double yValue2 = sElement.calcValue( this.getEndY(), yAxis, false );

			this.mStartXValue = sElement.getNumberInRangeOrder( xValue1, xAxis );
			this.mStartYValue = sElement.getNumberInRangeOrder( yValue1, yAxis );
			this.mEndXValue = sElement.getNumberInRangeOrder( xValue2, xAxis );
			this.mEndYValue = sElement.getNumberInRangeOrder( yValue2, yAxis );

//System.out.println(xValue1+"  "+xValue2);
//System.out.println(this.mStartXValue+"  "+this.mEndXValue);
//System.out.println();

			return true;
		}


		/**
		 * 
		 */
		public boolean setShapeWithAxesValues()
		{
			ShapeObject sh = this.mShape;
			SGAxis xAxis = this.getXAxis();
			SGAxis yAxis = this.getYAxis();
			
			SGShapeElement sElement = this.getShapeElement();
			
			final float x1 = sElement.calcLocation( this.mStartXValue, xAxis, true );
			final float y1 = sElement.calcLocation( this.mStartYValue, yAxis, false);
			final float x2 = sElement.calcLocation( this.mEndXValue, xAxis, true );
			final float y2 = sElement.calcLocation( this.mEndYValue, yAxis, false);

			if( Float.isNaN(x1) | Float.isNaN(y1) | Float.isNaN(x2) | Float.isNaN(y2) )
			{
				sh.setValid( false );
				return false;
			}
			else
			{
				sh.setValid( true );
			}

			this.setStartX(x1);
			this.setStartY(y1);
			this.setEndX(x2);
			this.setEndY(y2);

			return true;
		}


		/**
		 * 
		 * @return
		 */
		public ArrayList getAnchorPointList()
		{
			ArrayList list = new ArrayList();

			Point2D ps = new Point2D.Float( this.getStartX(), this.getStartY() );
			Point2D pe = new Point2D.Float( this.getEndX(), this.getEndY() );

			list.add(ps);
			list.add(pe);

			return list;
		}


		/**
		 * 
		 */
		public Object copy()
		{
			Arrow el = new Arrow();
			el.setShape( this.mShape );
			el.setMagnification( this.getMagnification() );
			el.setProperties( this.getProperties() );
			el.setShapeWithAxesValues();
//			el.mTempXAxis = mAxisElement.getConfigurationInCube( this.mShape.getXAxis() );
//			el.mTempYAxis = mAxisElement.getConfigurationInCube( this.mShape.getYAxis() );
			return el;
		}

		private int mTempXAxis = -1;
		private int mTempYAxis = -1;



		/**
		 * 
		 */
		public boolean drag( MouseEvent e, Point pos, final int ml )
		{
			final int diffX = e.getX() - pos.x;
			final int diffY = e.getY() - pos.y;
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY() + diffY
			);

			if( ml == START )
			{
				this.setStartX( this.getStartX() + diffX );
				this.setStartY( this.getStartY() + diffY );
			}
			else if( ml == END )
			{
				this.setEndX( this.getEndX() + diffX );
				this.setEndY( this.getEndY() + diffY );
			}

			return true;
		}


		/**
		 * 
		 */
		public void translate( final float dx, final float dy )
		{
			this.setStartX( this.getStartX() + dx );
			this.setStartY( this.getStartY() + dy );
			this.setEndX( this.getEndX() + dx );
			this.setEndY( this.getEndY() + dy );

			this.setAxisValuesWithShape();
		}


		/**
		 * 
		 */
		public String getName()
		{
			return NAME;
		}


		/**
		 * 
		 */
		public String getClassDescription()
		{
			return "";
		}


		/**
		 * 
		 * @return
		 */
		public String getInstanceDescription()
		{
			SGIAxisElement aElement = this.getShapeElement().getAxisElement();
			String xAxis = aElement.getAxisLocation( this.getXAxis() );
			String yAxis = aElement.getAxisLocation( this.getYAxis() );

			String startStr = "START=" + "(" + this.mStartXValue + ", " + this.mStartYValue + ")";
			String endStr = "END=" + "(" + this.mEndXValue + ", " + this.mEndYValue + ")";

			String str = this.getID() + ": " + this.getName() + ", ";
			str += xAxis + ", " + yAxis + ", " + startStr + ", " + endStr;

			return str;
		}


		/**
		 * 
		 */
		public ArrayList getChildNodes()
		{
			return new ArrayList();
		}


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

		
		/**
		 * 
		 */
		public boolean setMement( SGProperties p )
		{
			return this.setProperties(p);
		}


		/**
		 * 
		 */
		public SGProperties getProperties()
		{
			SGProperties p = new ArrowShapeProperties();
			if( this.getProperties(p) == false ) return null;
			return p;
		}


		/**
		 * 
		 */
		public boolean getProperties( SGProperties p )
		{
			if( ( p instanceof ArrowShapeProperties) == false )
			{
				return false;
			}
			
			if( super.getProperties(p) == false ) return false;

			ArrowShapeProperties rp = (ArrowShapeProperties)p;
			
			SGShapeElement sElement = this.getShapeElement();
			SGIAxisElement aElement = sElement.getAxisElement();

			SGAxis xAxis = this.getXAxis();
			SGAxis yAxis = this.getYAxis();
			rp.setXAxisLocation( aElement.getConfigurationInPlane(xAxis) );
			rp.setYAxisLocation( aElement.getConfigurationInPlane(yAxis) );

			rp.setStartXValue( this.mStartXValue );
			rp.setStartYValue( this.mStartYValue );
			rp.setEndXValue( this.mEndXValue );
			rp.setEndYValue( this.mEndYValue );

			return true;
		}


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

			if( super.setProperties(p) == false ) return false;

			ArrowShapeProperties rp = (ArrowShapeProperties)p;

			final Double x1 = rp.getStartXValue();
			if( x1==null ) return false;
			this.mStartXValue = x1.doubleValue();
			
			final Double y1 = rp.getStartYValue();
			if( y1==null ) return false;
			this.mStartYValue = y1.doubleValue();

			final Double x2 = rp.getEndXValue();
			if( x2==null ) return false;
			this.mEndXValue = x2.doubleValue();
			
			final Double y2 = rp.getEndYValue();
			if( y2==null ) return false;
			this.mEndYValue = y2.doubleValue();

//System.out.println(x1+"  "+x2);
//System.out.println(y1+"  "+y2);
//System.out.println();

			return true;
		}


		/**
		 * 
		 */
		private SGProperties mTemporaryProperties = null;


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


		/**
		 * 
		 */
		public boolean commit()
		{
			// ダイアログを出す前後でプロパティが変更されている場合のみ、
			// 履歴を更新する

			SGProperties pTemp = this.mTemporaryProperties;
			SGProperties pPresent = this.getProperties();
			if( pTemp.equals(pPresent) == false )
			{
				this.mShape.setChanged(true);
			}

			this.setShapeWithAxesValues();

			SGShapeElement sh = this.getShapeElement();
			sh.repaint();
			sh.notifyChange();

			return true;
		}


		/**
		 * 
		 */
		public boolean cancel()
		{
			if( this.setProperties( this.mTemporaryProperties ) == false )
			{
				return false;
			}
			this.mTemporaryProperties = null;

			this.setShapeWithAxesValues();

			SGShapeElement sh = this.getShapeElement();
			sh.repaint();
			sh.notifyChange();

			return true;
		}


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

			SGShapeElement sh = this.getShapeElement();
			sh.repaint();
			sh.notifyChange();

//this.dump();

			return true;
		}



		/**
		 * Returns a property dialog.
		 * @return property dialog
		 */
		public SGPropertyDialog getPropertyDialog()
		{
			SGShapeElement sh = this.getShapeElement();
			return sh.mArrowDialog;
		}



		//
		// property file
		//
		
		/**
		 * 
		 */
		public boolean writeProperty( final Element el )
		{
			String cm = SGUtilityNumber.cm;
			String pt = SGUtilityNumber.pt;
			String degree = SGUtilityNumber.degree;

			final float cp = SGIConstants.CM_POINT_RATIO;
			final float rd = SGIConstants.RADIAN_DEGREE_RATIO;

			el.setAttribute( KEY_START_X_VALUE, Double.toString( this.getStartXValue() ) );
			el.setAttribute( KEY_START_Y_VALUE, Double.toString( this.getStartYValue() ) );
			el.setAttribute( KEY_END_X_VALUE, Double.toString( this.getEndXValue() ) );
			el.setAttribute( KEY_END_Y_VALUE, Double.toString( this.getEndYValue() ) );

			el.setAttribute( KEY_LINE_WIDTH, Float.toString( this.getLineWidth() ) + pt );
			el.setAttribute( KEY_LINE_TYPE, SGDrawingElementLine.getLineTypeName( this.getLineType() ) );
			el.setAttribute( KEY_HEAD_SIZE, Float.toString( this.getHeadSize()*cp ) + cm );
			el.setAttribute( KEY_START_HEAD_TYPE, SGDrawingElementArrow.getSymbolTypeName( this.getStartHeadType() ) );
			el.setAttribute( KEY_END_HEAD_TYPE, SGDrawingElementArrow.getSymbolTypeName( this.getEndHeadType() ) );
			el.setAttribute( KEY_HEAD_OPEN_ANGLE, Double.toString( this.getHeadOpenAngle()/rd ) + degree );
			el.setAttribute( KEY_HEAD_CLOSE_ANGLE, Double.toString( this.getHeadCloseAngle()/rd ) + degree );
			el.setAttribute( KEY_COLOR_LIST, SGUtilityText.getColorListString( this.mColorList ) );

			return true;
		}


		/**
		 * 
		 */
		public boolean readProperty( final Element el )
		{
			final String degree = SGUtilityNumber.degree;
			final float rd = SGIConstants.RADIAN_DEGREE_RATIO;

			String str = null;
			Number num = null;
			Color cl = null;
			Boolean b = null;
			ArrayList list = null;


			// start x value
			str = el.getAttribute( KEY_START_X_VALUE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getDouble(str);
			if( num==null )
			{
				return false;
			}
			final double startXValue = num.doubleValue();


			// start y value
			str = el.getAttribute( KEY_START_Y_VALUE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getDouble(str);
			if( num==null )
			{
				return false;
			}
			final double startYValue = num.doubleValue();
			

			// end x value
			str = el.getAttribute( KEY_END_X_VALUE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getDouble(str);
			if( num==null )
			{
				return false;
			}
			final double endXValue = num.doubleValue();


			// end y value
			str = el.getAttribute( KEY_END_Y_VALUE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getDouble(str);
			if( num==null )
			{
				return false;
			}
			final double endYValue = num.doubleValue();


			// line width
			str = el.getAttribute( KEY_LINE_WIDTH );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getLengthInPoint( str );
			if( num==null )
			{
				return false;
			}
			final float lineWidth = num.floatValue();


			// line type
			str = el.getAttribute( KEY_LINE_TYPE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGDrawingElementLine.getLineTypeFromName(str);
			if( num==null )
			{
				return false;
			}
			final int lineType = num.intValue();


			// head size
			str = el.getAttribute( KEY_HEAD_SIZE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getLengthInPoint( str );
			if( num==null )
			{
				return false;
			}
			final float headSize = num.floatValue();


			// start head type
			str = el.getAttribute( KEY_START_HEAD_TYPE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGDrawingElementArrow.getSymbolTypeFromName(str);
			if( num==null )
			{
				return false;
			}
			final int startHeadType = num.intValue();


			// end head type
			str = el.getAttribute( KEY_END_HEAD_TYPE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGDrawingElementArrow.getSymbolTypeFromName(str);
			if( num==null )
			{
				return false;
			}
			final int endHeadType = num.intValue();


			// open angle
			str = el.getAttribute( KEY_HEAD_OPEN_ANGLE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getFloat(str,degree);
			if( num==null )
			{
				return false;
			}
			final float openAngle = num.floatValue()*rd;


			// close angle
			str = el.getAttribute( KEY_HEAD_CLOSE_ANGLE );
			if( str.length()==0 )
			{
				return false;
			}
			num = SGUtilityText.getFloat(str,degree);
			if( num==null )
			{
				return false;
			}
			final float closeAngle = num.floatValue()*rd;


			// color
			str = el.getAttribute( KEY_COLOR_LIST );
			if( str.length()==0 )
			{
				return false;
			}
			list = SGUtilityText.getColorList(str);
			if( list==null )
			{
				return false;
			}
			final ArrayList colorList = list;
		

			// set properties
			this.setStartXValue( startXValue );
			this.setStartYValue( startYValue );
			this.setEndXValue( endXValue );
			this.setEndYValue( endYValue );
			this.setLineWidth( lineWidth );
			this.setLineType( lineType );
			this.setHeadSize( headSize );
			this.setStartHeadType( startHeadType );
			this.setEndHeadType( endHeadType );
			this.setHeadOpenAngle( openAngle );
			this.setHeadCloseAngle( closeAngle );
			this.setColor( colorList );

			return true;
		}

	}



	/**
	 * Properties of rectangular shape.
	 */
	private static class ArrowShapeProperties
		extends ArrowProperties
		implements PropertyWithAxes
	{
		private double mStartXValue = 0.0;
		private double mStartYValue = 0.0;
		private double mEndXValue = 0.0;
		private double mEndYValue = 0.0;
		private int mXAxisLocation = -1;
		private int mYAxisLocation = -1;

		/**
		 * 
		 */
		public ArrowShapeProperties()
		{
			super();
		}

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

			if( super.equals(obj) == false ) return false;

			ArrowShapeProperties p = (ArrowShapeProperties)obj;

			if( p.mStartXValue!=this.mStartXValue ) return false;
			if( p.mStartYValue!=this.mStartYValue ) return false;
			if( p.mEndXValue!=this.mEndXValue ) return false;
			if( p.mEndYValue!=this.mEndYValue ) return false;
			if( this.mXAxisLocation!=p.mXAxisLocation ) return false;
			if( this.mYAxisLocation!=p.mYAxisLocation ) return false;

			return true;
		}


		public Double getStartXValue()
		{
			return new Double( this.mStartXValue );
		}
		
		public Double getStartYValue()
		{
			return new Double( this.mStartYValue );
		}

		public Double getEndXValue()
		{
			return new Double( this.mEndXValue );
		}
		
		public Double getEndYValue()
		{
			return new Double( this.mEndYValue );
		}

		public int getXAxisLocation()
		{
			return this.mXAxisLocation;
		}

		public int getYAxisLocation()
		{
			return this.mYAxisLocation;
		}

		public boolean setStartXValue( final double value )
		{
			this.mStartXValue = value;
			return true;
		}

		public boolean setStartYValue( final double value )
		{
			this.mStartYValue = value;
			return true;
		}

		public boolean setEndXValue( final double value )
		{
			this.mEndXValue = value;
			return true;
		}

		public boolean setEndYValue( final double value )
		{
			this.mEndYValue = value;
			return true;
		}

		private boolean setXAxisLocation( final int location )
		{
			this.mXAxisLocation = location;
			return true;
		}

		private boolean setYAxisLocation( final int location )
		{
			this.mYAxisLocation = location;
			return true;
		}

	}


}

