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

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Frame;
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.font.FontRenderContext;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

import javax.swing.JLabel;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;

import jp.riken.brain.ni.samuraigraph.base.SGAxis;
import jp.riken.brain.ni.samuraigraph.base.SGDefaultValues;
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.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.SGISelectable;
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.SGTuple2f;
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.SGFigureElement;
import jp.riken.brain.ni.samuraigraph.figure.SGIStringConstants;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementString.StringProperties;

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



/**
 * 文字列描画用パネルのクラス
 */

public class SGStringElement extends SGFigureElement
	implements SGIStringElement, SGIStringConstants, CaretListener, ActionListener
{

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

	
	
	/**
	 * 
	 */
	private ArrayList mLabelElementList = new ArrayList();


	/**
	 * 
	 */
	private LabelElement mEditedLabelElement = null;
	
	
	/**
	 * 
	 */
	private int mFocusedX = 0;


	/**
	 * 
	 */
	private int mFocusedY = 0;



	/**
	 * 
	 */
	private float mFrameLineWidth = 1.0f;



	/**
	 * 
	 */
	private JTextField mEditField = null;



	/**
	 * 
	 */
	public static final int BOUNDARY_LINE_WIDTH = 6;

	


	/**
	 * コンストラクタ
	 */
	public SGStringElement()
	{
		super();
		this.initEditField();
	}



	/**
	 * 
	 * @return
	 */
	public String toString()
	{
		return new String("SGStringElement");
	}


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


	/**
	 * 
	 */
	private boolean initEditField()
	{
		this.mEditField = new JTextField(10);
		JTextField tf = this.mEditField;

		this.setLayout(null);
		this.add(tf);
		tf.setVisible(false);
		tf.addActionListener(this);
		tf.addCaretListener(this);

		return true;
	}



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

	
	
	/**
	 * 引数で指定された点にテキストフィールドを出して、
	 * 文字列を追加する処理を行う
	 */
	public boolean addString( final int x, final int y )
	{

		// 
		if( this.getGraphRect().contains( x, y ) == false )
		{
			return false;
		}


		// 前処理
		this.clearFocusedObjects();

		this.mFocusedX = x;
		this.mFocusedY = y;

		JTextField tf = this.mEditField;

		tf.setVisible(true);

		final String fontName = SGDefaultValues.LABEL_FONT_NAME;
		final float fontSize = SGDefaultValues.LABEL_FONT_SIZE;
		final int fontStyle = SGUtilityText.getFontStyle( SGDefaultValues.LABEL_FONT_STYLE );
		Font font = new Font(
			fontName,
			fontStyle,
			(int)(this.mMagnification*fontSize) );
		tf.setFont( font );

		final String str = "   ";
		final Rectangle2D stringRect = font.getStringBounds(
			str, new FontRenderContext( null, false, false ) );

		final int lx = this.mFocusedX - this.mEditField.getInsets().left;
		final int ly = this.mFocusedY - (int)(this.mMagnification*fontSize/2.0f);
		final int width = (int)( stringRect.getWidth() + this.mMagnification*fontSize );
		final int height = (int)( stringRect.getHeight() + this.mMagnification*fontSize );

		tf.setLocation( lx, ly );
		tf.setSize( width, height );
		tf.setText("");
		tf.requestFocus();

		this.repaint();

		return true;

	}



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

		ArrayList list = this.getVisibleStringElementList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			final LabelElement el = (LabelElement)list.get(ii);

			// draw string
			el.drawString(g2d);

			// draw bounding box
			if( el.mFrameFlag )
			{
				el.drawBoundingBox( g2d );
			}
		}

		// draw symbols around all objects
		if( this.mSymbolsVisibleFlagAroundAllObjects )
		{
			for( int ii=0; ii<list.size(); ii++ )
			{
				LabelElement el = (LabelElement)list.get(ii);
				ArrayList pList = el.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++ )
			{
				LabelElement el = (LabelElement)fList.get(ii);
				ArrayList pList = el.getAnchorPointList();
				SGUtilityForFigureElement.drawAnchorAsFocusedObject( pList, g2d );
			}
		}

		
		// テキストフィールドの境界線を描画
		JTextField tf = this.mEditField;
		if( tf.isVisible() )
		{
			g2d.setPaint( Color.GRAY );
			g2d.setStroke( new BasicStroke( BOUNDARY_LINE_WIDTH ) );
			g2d.draw( tf.getBounds() );
			g2d.setStroke( new BasicStroke(2) );
			tf.repaint();
		}

	}



	/**
	 * 
	 */
	private ArrayList getVisibleStringElementList()
	{
		ArrayList list = new ArrayList();

		ArrayList lList = this.mLabelElementList;
		for( int ii=0; ii<lList.size(); ii++ )
		{
			LabelElement el = (LabelElement)lList.get(ii);
			if( el.isVisible() )
			{
				list.add( el );
			}
		}

		return list;
	}



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


	/**
	 * 
	 */
	public boolean getFocusedObjectsList( ArrayList list )
	{
		ArrayList elList = this.getVisibleStringElementList();
		for( int ii=0; ii<elList.size(); ii++ )
		{
			SGISelectable s = (SGISelectable)elList.get(ii);
			if( s.isSelected() )
			{
				list.add(s);
			}
		}
		return true;
	}


	/**
	 * 
	 *
	 */
	public boolean hideSelectedObject( SGISelectable s )
	{
		LabelElement el = (LabelElement)s;
		el.setVisible(false);
		return true;
	}


	/**
	 * 
	 */
	private boolean setVisibleStringElement( final ArrayList listVisible )
	{
		return this.setVisibleDrawingElement( this.mLabelElementList, listVisible );
	}





	/**
	 * 同期を取る。
	 */
	public boolean synchronize( final SGIFigureElement element )
	{

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

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


		return flag;
	}


	
	private boolean synchronizedToAxisElement( final SGIAxisElement element )
	{
/*		
		ArrayList list = this.getVisibleStringElementList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			ElementString el = (ElementString)list.get(ii);
			el.setAxisValue();
		}
*/
		return true;
	}
	
	
	

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



	/**
	 * ズーム
	 */
	public boolean zoom( final float ratio )
	{
		
		if( this.terminateEditField() == false )
		{
			return false;
		}
		
		if( super.zoom(ratio) == false )
		{
			return false;
		}

		ArrayList list = this.mLabelElementList;
		for( int ii=0; ii<list.size(); ii++ )
		{
			final LabelElement element = (LabelElement)list.get(ii);
			element.zoom(ratio);
		}

		return true;
	}




	/**
	 * 
	 */
	public boolean setGraphRect(
		final float x, final float y, final float w, final float h )
	{
		if( super.setGraphRect(x,y,w,h) == false )
		{
			return false;
		}
		
		if( this.terminateEditField() == false )
		{
			return false;
		}

		ArrayList list = this.mLabelElementList;
		for( int ii=0; ii<list.size(); ii++ )
		{
			LabelElement el = (LabelElement)list.get(ii);
			el.updateLocation();
		}

		return true;
	}





	/**
	 * 
	 */
	public boolean getMarginAroundGraphRect(
		SGTuple2f topAndBottom, SGTuple2f leftAndRight )
	{

		//
		if( super.getMarginAroundGraphRect( topAndBottom, leftAndRight ) == false )
		{
			return false;
		}


		Rectangle2D graphRect = this.getGraphRect();

		ArrayList sList = this.getVisibleStringElementList();
		ArrayList rectList = new ArrayList();
		for( int ii=0; ii<sList.size(); ii++ )
		{
			LabelElement el = (LabelElement)sList.get(ii);
			rectList.add( el.getElementBounds() );
		}

		if( rectList.size()==0 )
		{
			return true;
		}


		Rectangle2D sRect = SGUtility.createUnion(rectList);

		ArrayList list = new ArrayList();
		list.add( graphRect );
		list.add( sRect );

		Rectangle2D uniRect = SGUtility.createUnion(list);

//System.out.println(graphRect);
//System.out.println(lRect);
//System.out.println(uniRect);		

		final float top = (float)( graphRect.getY() - uniRect.getY() );
		final float bottom = (float)( ( uniRect.getY() + uniRect.getHeight() )
			- ( graphRect.getY() + graphRect.getHeight() ) );
		final float left = (float)( graphRect.getX() - uniRect.getX() );
		final float right = (float)( ( uniRect.getX() + uniRect.getWidth() )
			- ( graphRect.getX() + graphRect.getWidth() ) );

		topAndBottom.x += top;
		topAndBottom.y += bottom;
		leftAndRight.x += left;
		leftAndRight.y += right;

//System.out.println(topAndBottom+"  "+leftAndRight);

//System.out.println();

		return true;
	}




	/**
	 * 
	 */
	public boolean onMouseClicked( final MouseEvent e )
	{

		final int x = e.getX();
		final int y = e.getY();
		final int mod = e.getModifiers();
		final int cnt = e.getClickCount();
		final boolean onEdgeFlag = this.onEdge(x,y);
		final boolean ctrl = (mod&MouseEvent.CTRL_MASK)!=0;
		final boolean shift = (mod&MouseEvent.SHIFT_MASK)!=0;

		ArrayList list = this.getVisibleStringElementList();
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final LabelElement el = (LabelElement)list.get(ii);

			if( onEdgeFlag && el.equals( this.mEditedLabelElement ) )
			{
				if( SwingUtilities.isLeftMouseButton(e) & cnt==2 )
				{
					this.setPropertiesOfSelectedObjects();
					this.hideEditField();
				}
				return true;
			}

			if( el.contains(x,y) )
			{
				if( SwingUtilities.isLeftMouseButton(e) )
				{
					if( cnt==1 )
					{
						if( el.isSelected() & !ctrl & !shift )
						{
							this.mEditedLabelElement = el;
							this.mFocusedX = (int)el.getX();
							this.mFocusedY = (int)el.getY();
							this.showEditField( el );
						}
						else
						{
							this.updateFocusedObjectsList(el,e);
						}
					}
				}
				else if( SwingUtilities.isRightMouseButton(e) & cnt==1 )
				{
					this.updateFocusedObjectsList(el,e);
					el.getPopupMenu().show( this, x, y );
				}

				return true;
			}

		}

		return false;

	}



	/**
	 * 
	 */
	private boolean terminateEditField()
	{

		if( this.mEditedLabelElement==null )
		{
			if( this.addStringElementFromTextField() == false )
			{
				return false;
			}
		}
		else
		{
			if( this.commitEdit() == false )
			{
				return false;
			}
		}

		return true;
	}



	/**
	 * 
	 * @return
	 */
	private boolean commitEdit()
	{
		String before = this.mEditedLabelElement.getString();
		String after = this.mEditField.getText();
		if( SGUtilityText.isValidString(after) )
		{
			this.mEditedLabelElement.setString( after );
			if( before.equals(after) == false )
			{
				this.mEditedLabelElement.setChanged(true);
				this.notifyToRoot();
			}
		}

		this.mEditedLabelElement = null;
		this.hideEditField();
		this.repaint();
		
		return true;
	}



	/**
	 * 
	 */
	private boolean hideEditField()
	{
		this.mEditField.setText("");
		this.mEditField.setVisible(false);

		return true;
	}



	/**
	 * 
	 * @return
	 */
/*	public boolean clearFocusedObjects()
	{
		if( super.clearFocusedObjects() == false )
		{
			return false;
		}
		
		// 
		if( this.mEditField.isVisible() )
		{
			this.terminateEditField();
		}

		this.mEditedLabelElement = null;
		return true;
	}
*/



	/**
	 * 
	 */
	private boolean showEditField( final LabelElement el )
	{
		final JTextField tf = this.mEditField;

		final Rectangle2D rect = el.getElementBounds();
		final Rectangle2D sRect = el.getStringRect();

		final float fontSize = el.getMagnification()*el.getFontSize();

		final int x = (int)( rect.getX() - tf.getInsets().left );
		final int y = (int)( rect.getY() - fontSize/2.0f );
		final int w = (int)( sRect.getWidth() + fontSize );
		final int h = (int)( sRect.getHeight() + fontSize );

		tf.setLocation( x, y );
		tf.setSize( w, h );
		tf.setFont( new Font( el.getFontName(), el.getFontStyle(), (int)fontSize ) );
		tf.setForeground( el.getColor(0) );
		tf.setText( el.getString() );

		// show the text field
		tf.setVisible(true);
		tf.requestFocus();
		tf.setCaretPosition(0);

		return true;

	}


	/**
	 * 
	 */
	private Point mPressedPoint = null;

	private boolean mDraggableFlag = false;
	
	
	/**
	 * 
	 * @param e
	 */
	public boolean onMousePressed( final MouseEvent e )
	{
		final int x = e.getX();
		final int y = e.getY();

		ArrayList fList = this.getFocusedObjectsList();
		ArrayList list = this.getVisibleStringElementList();
		boolean flag = false;
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final LabelElement el = (LabelElement)list.get(ii);
			if( el.contains(x,y) )
			{
				el.mFrameFlag = true;
				if( fList.contains(el) )
				{
					this.mPressedPoint = e.getPoint();
					el.mTemporaryProperties = el.getProperties();
					this.mDraggableFlag = true;
				}
				flag = true;
				break;
			}
		}


		// if the mouse is pressed on the label
		if( flag )
		{
			this.setMouseCursor( Cursor.MOVE_CURSOR );
		}
		// otherwise
		else
		{
			final boolean onEdgeFlag = this.onEdge(x,y);
			
			// if the mouse is on the edge of the text field
			if( this.mEditedLabelElement!=null && onEdgeFlag )
			{
				flag = true;
			}
			else
			{
				// hide the edit field
				if( this.mEditField.isVisible() )
				{
					this.terminateEditField();
				}
				else
				{
//					this.clearFocusedObjects();
				}
			}
			
		}
		
		return flag;
	}



	/**
	 * 
	 * @param e
	 */
	public boolean onMouseDragged( final MouseEvent e )
	{

		if( this.mPressedPoint==null )
		{
			return false;
		}
		if( this.mDraggableFlag == false)
		{
			return false;
		}
		this.hideEditField();

		// translate label all selected elements
		final int dx = e.getX() - this.mPressedPoint.x;
		final int dy = e.getY() - this.mPressedPoint.y;

		ArrayList list = this.getFocusedObjectsList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			LabelElement el = (LabelElement)list.get(ii);
			el.translate(dx,dy);
		}

		// update the temporary object
		this.mPressedPoint = e.getPoint();
		
		return true;
	}


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

		ArrayList list = this.getFocusedObjectsList();
		boolean contained = false;
		boolean changed = false;
		for( int ii=0; ii<list.size(); ii++ )
		{
			LabelElement el = (LabelElement)list.get(ii);
/*			
			SGProperties temp = el.mTemporaryProperties;
			SGProperties p = el.getProperties();
			if( p.equals(temp)==false )
			{
				el.setChanged(true);
				changed = true;
			}
*/
			Rectangle2D rect = el.getElementBounds();
			contained |= rect.contains( e.getPoint() );
		}

		if( contained )
		{
			setMouseCursor( Cursor.HAND_CURSOR );
		}
		else
		{
			setMouseCursor( Cursor.DEFAULT_CURSOR );
		}
/*
		if( changed )
		{
			this.notifyToRoot();
		}
*/
		this.mDraggableFlag = false;
		return true;
	}

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



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

	
	
	/**
	 * 
	 */
	public boolean onEdge( final int x, final int y )
	{
		if( this.mEditField.isVisible()==false )
		{
			return false;
		}

		final int bWidth = BOUNDARY_LINE_WIDTH;
		final Rectangle rect = this.mEditField.getBounds();
		final int xx = rect.x - bWidth;
		final int yy = rect.y - bWidth;
		final int ww = rect.width + 2*bWidth;
		final int hh = rect.height + 2*bWidth;
		final Rectangle rectNew = new Rectangle( xx, yy, ww, hh );

		final boolean flagShape =  rect.contains( x, y );
		final boolean flagRectNew = rectNew.contains( x, y );

		final boolean flag = (!flagShape)&(flagRectNew);
		
		return flag;
	}



	/**
	 * 
	 * @param e
	 */
	public boolean onDrawingElement( final int x, final int y )
	{
		ArrayList list = this.getVisibleStringElementList();
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final LabelElement el = (LabelElement)list.get(ii);
			final boolean flag = el.contains(x,y);
			el.mFrameFlag = flag;
			if( flag )
			{
				this.setMouseCursor( Cursor.HAND_CURSOR );
				return true;
			}
		}

		return false;
	}




	/**
	 * 
	 */
	public void actionPerformed(final ActionEvent e)
	{

		final Object source = e.getSource();

		if( source.equals( this.mEditField ) )
		{
			this.terminateEditField();
		}

	}



	/**
	 * 
	 */
	public void caretUpdate( final CaretEvent e )
	{

		JTextField tf = this.mEditField;
		final String str = tf.getText();

		float fontSize;
		Font font = null;

		// edit
		if( this.mEditedLabelElement != null )
		{
			LabelElement el = this.mEditedLabelElement;
			fontSize = el.getFontSize();
			font = new Font(
				el.getFontName(),
				el.getFontStyle(),
				(int)(el.getMagnification()*fontSize) );
		}
		// add
		else
		{
			fontSize = SGDefaultValues.LABEL_FONT_SIZE;
			font = new Font(
				SGDefaultValues.LABEL_FONT_NAME,
				SGUtilityText.getFontStyle( SGDefaultValues.LABEL_FONT_STYLE ),
				(int)(this.mMagnification*fontSize) );
		}

		Rectangle2D stringRect = font.getStringBounds(
			str, new FontRenderContext( null, false, false ) );


		final double width = stringRect.getWidth();
		if( width > tf.getWidth() )
		{
			tf.setSize(
				(int)( stringRect.getWidth() + this.mMagnification*fontSize ),
				tf.getHeight()
			);
		}


		this.repaint();
	}



	/**
	 * 
	 */
	private boolean addStringElementFromTextField()
	{

		final String str = this.mEditField.getText();

		if( SGUtilityText.isValidString(str) )
		{
			//
			final LabelElement es
				= new LabelElement( this.mEditField.getText() );
			es.setMagnification( this.mMagnification );
			es.setLocation( this.mFocusedX, this.mFocusedY );
			SGAxis xAxis = mAxisElement.getAxis( SGDefaultValues.AXIS_BREAK_HORIZONTAL_AXIS );
			SGAxis yAxis = mAxisElement.getAxis( SGDefaultValues.AXIS_BREAK_PERPENDICULAR_AXIS );
			es.mXAxis = xAxis;
			es.mYAxis = yAxis;

			this.addToList(es);

			// initialize history
			es.initPropertiesHistory();

			this.setChanged(true);

			this.notifyToRoot();
		}

		this.hideEditField();

		this.repaint();


		return true;
	}



	private int mIDCounter = 0;

	private void addToList( LabelElement el )
	{
		this.mLabelElementList.add(el);
		el.mID = this.mIDCounter;
		this.mIDCounter++;
	}
	


	/**
	 * 
	 */
	private boolean removeString( final LabelElement string )
	{
		ArrayList list = this.mLabelElementList;
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final LabelElement el = (LabelElement)list.get(ii);
			if( el.equals(string) )
			{
				list.remove(string);
				return true;
			}
		}

		return false;
	}



	/**
	 * Move the focused objects to front or back.
	 * @param toFront - flag whether to front or back
	 * @return true:succeeded, false:failed
	 */
	public boolean moveFocusedObjects( boolean toFront )
	{
		ArrayList list = this.getFocusedObjectsList();

		if( toFront )
		{
			for( int ii=0; ii<list.size(); ii++ )
			{
				LabelElement el = (LabelElement)list.get(ii);
				this.moveLabelToFront(el);
			}
		}
		else
		{
			for( int ii=0; ii<list.size(); ii++ )
			{
				LabelElement el = (LabelElement)list.get(ii);
				this.moveLabelToBack(el);
			}
		}

		return true;
	}



	/**
	 * 
	 */
	private boolean moveLabelToFront( final LabelElement el )
	{
		return this.moveObjectToFront( el, this.mLabelElementList );
	}


	/**
	 * 
	 */
	private boolean moveLabelToBack( final LabelElement el )
	{
		return this.moveObjectToBack( el, this.mLabelElementList );
	}



	/**
	 * 
	 * @param el
	 * @return
	 */
	private boolean hideLabel( final LabelElement el )
	{
		return this.hideObject(el);
	}



	/**
	 * 
	 * @return
	 */
	public String getTagName()
	{
		return TAG_NAME_STRING_ELEMENT;
	}

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

		ArrayList list = this.getVisibleStringElementList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			LabelElement label = (LabelElement)list.get(ii);
			Element elLabel =  label.createElement( document );
			if( elLabel==null )
			{
				return null;
			}
			el.appendChild( elLabel );
		}
		return el;
	}

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

	
	
	
	/**
	 * 
	 */
	public boolean readProperty( final Element element )
	{

		NodeList nList = element.getElementsByTagName( LabelElement.TAG_NAME_LABEL );
		for( int ii=0; ii<nList.getLength(); ii++ )
		{
			Node node = nList.item(ii);
			if( node instanceof Element )
			{
				Element el = (Element)node;
				LabelElement label = new LabelElement();
				if( label.readProperty(el) == false )
				{
					return false;
				}
				label.initPropertiesHistory();
				this.addToList( label );
			}
		}
		
		return true;
	}



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

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

		return true;
	}



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

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

		return true;
	}

	
	
	/**
	 * 
	 */
	public boolean updateHistory()
	{
		return this.updateHistory_( this.getVisibleStringElementList() );
	}



	/**
	 * 
	 */
	public boolean isChanged()
	{
		if( super.isChanged() )
		{
			return true;
		}
		ArrayList list = this.getVisibleStringElementList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			LabelElement el = (LabelElement)list.get(ii);
			if( el.isChanged() )
			{
				return true;
			}
		}
		return false;
	}

	

	/**
	 *
	 */
	private SGStringElementDialog mDialog = null;
	
	
	/**
	 * 
	 */
	private boolean createDialog()
	{
	
		final SGStringElementDialog dg = new SGStringElementDialog( mDialogOwner, true );
	
		this.mDialog = dg;
	
		return true;
	}
	
	/**
	 * 
	 * @return
	 */
	public boolean setDialogOwner( final Frame frame )
	{
		super.setDialogOwner(frame);
		this.createDialog();
		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
			LabelElement el = (LabelElement)cList.get(ii);
			
			// translate 
			el.translate( ox, oy );

			// set selected
			el.setSelected(true);

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

			// initialize history
			el.initPropertiesHistory();
		}

		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 LabelElement )
			{
				LabelElement label = (LabelElement)obj;

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

				SGProperties p = label.getProperties();
				
				LabelElement el = new LabelElement();
				el.setMagnification(mag);
				el.setProperties(p);

				el.mXAxis = this.mAxisElement.getAxisInCube( label.mTempXAxis );
				el.mYAxis = this.mAxisElement.getAxisInCube( label.mTempYAxis );

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

				// initialize history
				el.initPropertiesHistory();

				cnt++;
			}
		}

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

//		this.repaint();

		return true;
	}



	/**
	 * 
	 * @return
	 */
	public ArrayList getPropertyDialogObserverList()
	{
		return this.getFocusedObjectsList();
	}



	/**
	 * 
	 */
	class LabelElement extends SGDrawingElementString2DExtended
		implements ActionListener,
			SGIUndoable, SGISelectable, SGIMovable, SGICopiable,
			SGILabelDialogObserver, SGINode
	{

		/**
		 * 
		 */
		private int mID;


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


		/**
		 * 
		 */
		private SGAxis mXAxis = null;
		
		
		/**
		 * 
		 */
		private SGAxis mYAxis = null;

		
		/**
		 *
		 */
		private JPopupMenu mPopupMenu = new JPopupMenu();



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



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


		/**
		 * 
		 */
		LabelElement()
		{
			super();
			this.init();
		}


		/**
		 * 
		 */
		LabelElement( final String str )
		{
			super(str);
			this.init();
		}


		/**
		 * 
		 */
		private boolean init()
		{
			final int style = SGUtilityText.getFontStyle( SGDefaultValues.LABEL_FONT_STYLE );
			if( style==-1 )
			{
				return false;
			}
			this.setFont(
				SGDefaultValues.LABEL_FONT_NAME,
				style,
				SGDefaultValues.LABEL_FONT_SIZE
			);

			this.setAngle( SGDefaultValues.LABEL_ANGLE );
			this.setColor( SGDefaultValues.LABEL_COLOR );

			this.createPopupMenu();

			return true;
		}


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


		/**
		 * 
		 * @return
		 */
		public String getInstanceDescription()
		{
			String xAxis = SGStringElement.this.mAxisElement.getAxisLocation( this.mXAxis );
			String yAxis = SGStringElement.this.mAxisElement.getAxisLocation( this.mYAxis );

			String str = "";
			str += this.mID + ": " + this.getString() + ", ";
			str += xAxis + ", " + yAxis + ", ";
			str += "X=" + this.getXValue() + ", Y=" + this.getYValue();
			return str;
		}


		/**
		 * 
		 */
		public Object copy()
		{
			LabelElement el = new LabelElement();
			el.setMagnification( this.mMagnification );
			el.setProperties( this.getProperties() );
			el.setLocation( this.getX(), this.getY() );
			el.mTempXAxis = mAxisElement.getConfigurationInCube( this.mXAxis );
			el.mTempYAxis = mAxisElement.getConfigurationInCube( this.mYAxis );
			return el;
		}


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


		/**
		 * 
		 */
		LabelElement( final LabelElement element )
		{
			super(element);

			this.mX = element.mX;
			this.mY = element.mY;
			this.mMagnification = element.mMagnification;

			this.updateLocation();

			this.createPopupMenu();
		}

		
		
		/**
		 * 
		 * @return
		 */
		public double getXValue()
		{
			SGAxis axis = this.mXAxis;
			double value = calcValue( this.getX(), axis, true );
			value = getNumberInRangeOrder( value, axis );
			return value;
		}

		
		/**
		 * 
		 * @return
		 */
		public double getYValue()
		{
			SGAxis axis = this.mYAxis;
			double value = calcValue( this.getY(), axis, false );
			value = getNumberInRangeOrder( value, axis );
			return value;
		}

		
		/**
		 * 
		 * @param value
		 */
		public void setXValue( final double value )
		{
			SGAxis axis = this.mXAxis;
			
			// current values gotten from the position of legend
			double currentValue = calcValue( this.getX(), axis, true );
			currentValue = getNumberInRangeOrder( currentValue, axis );


			// if values from the dialog is diffrent from the current values,
			// set the values from the dialog

			float x;
			if( value==currentValue )
			{
				x = this.mX;
			}
			else
			{
				final float x_ = calcLocation( value, axis, true );
				x = ( x_ - mGraphRectX )/this.mMagnification;
			}

			this.mX = x;

			this.updateLocation();
		}


		/**
		 * 
		 * @param value
		 */
		public void setYValue( final double value )
		{
			SGAxis axis = this.mYAxis;
			
			// current values gotten from the position of legend
			double currentValue = calcValue( this.getY(), axis, false );
			currentValue = getNumberInRangeOrder( currentValue, axis );


			// if values from the dialog is diffrent from the current values,
			// set the values from the dialog

			float y;
			if( value==currentValue )
			{
				y = this.mY;
			}
			else
			{
				final float y_ = calcLocation( value, axis, false );
				y = ( y_ - mGraphRectY )/this.mMagnification;
			}

			this.mY = y;

			this.updateLocation();
		}



		/**
		 * 
		 * @param config
		 * @param value
		 * @return
		 */
		public boolean hasValidXValue( final int config, final Number value )
		{
			final SGAxis axis = (config==-1) ? this.mXAxis : mAxisElement.getAxisInPlane( config );
			final double v = (value!=null) ? value.doubleValue() : this.getXValue();
			return isValidValue( axis, v );
		}

		/**
		 * 
		 * @param config
		 * @param value
		 * @return
		 */
		public boolean hasValidYValue( final int config, final Number value )
		{
			final SGAxis axis = (config==-1) ? this.mYAxis : mAxisElement.getAxisInPlane( config );
			final double v = (value!=null) ? value.doubleValue() : this.getYValue();
			return isValidValue( axis, v );
		}


		
		/**
		 * 
		 */
		public int getXAxisConfiguration()
		{
			return SGStringElement.this.mAxisElement.getConfigurationInPlane( this.mXAxis );
		}


		/**
		 * 
		 */
		public int getYAxisConfiguration()
		{
			return SGStringElement.this.mAxisElement.getConfigurationInPlane( this.mYAxis );
		}



		/**
		 * 
		 */
		public void setAxisConfiguration( final int configX, final int configY )
		{
			SGIAxisElement aElement = SGStringElement.this.mAxisElement;
			SGAxis xAxis = aElement.getAxisInPlane( configX );
			SGAxis yAxis = aElement.getAxisInPlane( configY );
			this.mXAxis = xAxis;
			this.mYAxis = yAxis;
		}

		
		
		/**
		 * 
		 * @param color
		 */
		public void setStringColor( final Color color )
		{
			this.setColor( color );
		}


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

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

		
		/**
		 * 
		 * @param g2d
		 */
		private void drawBoundingBox( final Graphics2D g2d )
		{
			g2d.setStroke( new BasicStroke(1) );
			g2d.setPaint( Color.BLACK );
			g2d.draw( this.getElementBounds() );
		}



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

			Rectangle2D rect = this.getElementBounds();
			final float x = (float)rect.getX();
			final float y = (float)rect.getY();
			final float w = (float)rect.getWidth();
			final float h = (float)rect.getHeight();
			
			Point2D nw = new Point2D.Float(x,y);
			Point2D sw = new Point2D.Float(x,y+h);
			Point2D ne = new Point2D.Float(x+w,y);
			Point2D se = new Point2D.Float(x+w,y+h);

			list.add( nw );
			list.add( sw );
			list.add( ne );
			list.add( se );

			return list;
		}


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

			p.add( new JLabel( "  -- Label --" ) );
			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;
		}


		/**
		 * 
		 */
		private JPopupMenu getPopupMenu()
		{
			return mPopupMenu;
		}



		/**
		 * 
		 * @return
		 */
		public String getTagName()
		{
			return LabelElement.TAG_NAME_LABEL;
		}
		

		/**
		 * 
		 * @param document
		 * @return
		 */
		public Element createElement( final Document document )
		{
			Element el = document.createElement( this.getTagName() );
			if( this.writeProperty(el) == false )
			{
				return null;
			}
			return el;
		}
		
		
		/**
		 * 
		 * @param el
		 * @return
		 */
		public boolean writeProperty( final Element el )
		{
			String cm = SGUtilityNumber.cm;
			String pt = SGUtilityNumber.pt;
			String degree = SGUtilityNumber.degree;

			el.setAttribute( KEY_X_AXIS_POSITION, mAxisElement.getAxisLocation( this.mXAxis ) );
			el.setAttribute( KEY_Y_AXIS_POSITION, mAxisElement.getAxisLocation( this.mYAxis ) );

			
//			el.setAttribute( KEY_X, Float.toString( this.mX*SGIConstants.CM_POINT_RATIO ) + cm );
//			el.setAttribute( KEY_Y, Float.toString( this.mY*SGIConstants.CM_POINT_RATIO) + cm );

			el.setAttribute( KEY_X_VALUE, Double.toString( this.getXValue() ) );
			el.setAttribute( KEY_Y_VALUE, Double.toString( this.getYValue() ) );
			el.setAttribute( KEY_TEXT, this.getString() );

			el.setAttribute( KEY_FONT_NAME, this.getFontName() );
			el.setAttribute( KEY_FONT_SIZE, Float.toString( this.getFontSize() ) + pt );
			el.setAttribute( KEY_FONT_STYLE, SGUtilityText.getFontStyleName( this.getFontStyle() ) );
			el.setAttribute( KEY_STRING_COLORS, SGUtilityText.getColorListString( this.getColorList() ) );
			el.setAttribute( KEY_ANGLE, Float.toString( this.getAngle()/RADIAN_DEGREE_RATIO ) + degree );
			return true;
		}
		
		

		/**
		 * 
		 */
		public boolean readProperty( final Element element )
		{
			
			SGProperties p = this.readProperty_( element );
			if( p==null )
			{
				return false;
			}

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

		
		
		/**
		 * 
		 */
		private SGStringElement.LabelProperties readProperty_( final Element el )
		{
		
			final String cm = SGUtilityNumber.cm;
			final String pt = SGUtilityNumber.pt;
			final String degree = SGUtilityNumber.degree;
			final float ratio = SGIConstants.CM_POINT_RATIO;

			String str = null;
			Number num = null;
			Color cl = null;
			Boolean b = null;
			ArrayList list = null;
		
/*
			// x-coordinate
			str = el.getAttribute( KEY_X );
			if( str.length()==0 )
			{
				return null;
			}
			num = SGUtilityText.getFloat(str,cm);
			if( num==null )
			{
				return null;
			}
			final float x = num.floatValue()/ratio;

			
			// y-coordinate
			str = el.getAttribute( KEY_Y );
			if( str.length()==0 )
			{
				return null;
			}
			num = SGUtilityText.getFloat(str,cm);
			if( num==null )
			{
				return null;
			}
			final float y = num.floatValue()/ratio;
*/		

			// axes
			str = el.getAttribute( KEY_X_AXIS_POSITION );
			if( str.length()==0 )
			{
				return null;
			}
			SGAxis xAxis = mAxisElement.getAxis(str);
			
			str = el.getAttribute( KEY_Y_AXIS_POSITION );
			if( str.length()==0 )
			{
				return null;
			}
			SGAxis yAxis = mAxisElement.getAxis(str);

			
			str = el.getAttribute( KEY_X_VALUE );
			if( str.length()==0 )
			{
				return null;
			}
			num = SGUtilityText.getDouble(str);
			if( num==null )
			{
				return null;
			}
			final double xValue = num.doubleValue();
			final float posX = calcLocation( xValue, xAxis, true );
			final float x = ( posX - mGraphRectX )/this.mMagnification;
			
			
			str = el.getAttribute( KEY_Y_VALUE );
			if( str.length()==0 )
			{
				return null;
			}
			num = SGUtilityText.getDouble(str);
			if( num==null )
			{
				return null;
			}
			final double yValue = num.doubleValue();
			final float posY = calcLocation( yValue, yAxis, false );
			final float y = ( posY - mGraphRectY )/this.mMagnification;
			
			
			// set the text
			str = el.getAttribute( KEY_TEXT );
			if( str.length()==0 )
			{
				return null;
			}
			final String text = str;
		
		
			// set font name
			str = el.getAttribute( KEY_FONT_NAME );
			if( str.length()==0 )
			{
				return null;
			}
			final String fontName = str;
		
		
			// set font size
			str = el.getAttribute( KEY_FONT_SIZE );
			if( str.length()==0 )
			{
				return null;
			}
			num = SGUtilityText.getFloat(str,pt);
			if( num==null )
			{
				return null;
			}
			final float fontSize = num.floatValue();
		
		
			// set font style
			str = el.getAttribute( KEY_FONT_STYLE );
			if( str.length()==0 )
			{
				return null;
			}
			final int fontStyle = SGUtilityText.getFontStyle(str);
			if( fontStyle==-1 )
			{
				return null;
			}
		
		
			// set color
			str = el.getAttribute( KEY_STRING_COLORS );
			if( str.length()==0 )
			{
				return null;
			}
			list = SGUtilityText.getColorList(str);
			if( list==null )
			{
				return null;
			}
			final ArrayList colorList = list;
		
		
			// set angle
			str = el.getAttribute( KEY_ANGLE );
			if( str.length()==0 )
			{
				return null;
			}
			num = SGUtilityText.getFloat(str,degree);
			if( num==null )
			{
				return null;
			}
			final float angle = num.floatValue()*RADIAN_DEGREE_RATIO;
		
		
			LabelProperties p = new LabelProperties();
			p.setX(x);
			p.setY(y);
			p.setText(text);
			p.setFontName(fontName);
			p.setFontSize(fontSize);
			p.setFontStyle(fontStyle);
			p.setColorList(colorList);
			p.setAngle(angle);
			p.mXAxis = xAxis;
			p.mYAxis = yAxis;

			
			//
			p.setVisible(true);
			
			return p;
		
		}


		
		/**
		 * 
		 */
		public SGTuple2f getLocation()
		{
			final float x = this.mMagnification*this.mX + mGraphRectX;
			final float y = this.mMagnification*this.mY + mGraphRectY;
			return new SGTuple2f(x,y);
		}


		/**
		 * 
		 */
		public boolean setLocation( final float x, final float y)
		{
			final float dx = x - this.getX();
			final float dy = y - this.getY();

			this.mX = ( x - mGraphRectX )/this.mMagnification;
			this.mY = ( y - mGraphRectY )/this.mMagnification;

			ArrayList list = this.getAllStringElement();
			for( int ii=0; ii<list.size(); ii++ )
			{
				SGDrawingElementString2D el
					= (SGDrawingElementString2D)list.get(ii);
				el.translate( dx, dy );
			}

			return true;
		}


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


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

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

			LabelProperties lp = (LabelProperties)p;

			lp.setX( this.mX );
			lp.setY( this.mY );
			lp.setXAxis( this.mXAxis );
			lp.setYAxis( this.mYAxis );
			lp.setText( this.mString );

			return true;
		}



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

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

			LabelProperties lp = (LabelProperties)p;

			this.mX = lp.getX().floatValue();
			this.mY = lp.getY().floatValue();
			this.mXAxis = lp.getXAxis();
			this.mYAxis = lp.getYAxis();
			this.setString( lp.getText() );
			
			return true;
		}

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


		private boolean mChangedFlag = false;

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

		

		/**
		 * 
		 */
		public void actionPerformed( final ActionEvent e )
		{

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

			if( command.equals( MENUCMD_PROPERTY ) )
			{
				SGStringElement.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 boolean commit()
		{
			// ダイアログを出す前後でプロパティが変更されている場合のみ、
			// 履歴を更新する

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

			this.mTemporaryProperties = null;
			this.mFrameFlag = false;

			notifyChange();

			return true;
		}


		/**
		 * 
		 */
		public boolean preview()
		{
			repaint();
			notifyChange();
			return true;
		}


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

			repaint();
			notifyChange();

			return true;
		}


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


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



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



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



		/**
		 * 
		 */
		public boolean initPropertiesHistory()
		{
			this.addStringPropertyHistory( this.getProperties() );
			return true;
		}



		/**
		 * 
		 * @return
		 */
		private boolean addStringPropertyHistory( SGProperties p )
		{

			if( ( p instanceof LabelProperties ) == false )
			{
				return false; 
			}

//System.out.println("<< addFigurePropertyHistory >>");
//System.out.println(this.mFigureStateCounter);

			ArrayList list = new ArrayList();
			for( int ii=0; ii<this.mStringElementStateCounter; ii++ )
			{
				list.add( this.mStringElementPropertyHistoryList.get(ii) );
			}
			list.add(p);

			this.mStringElementPropertyHistoryList = list;

//System.out.println(this.mFigurePropertyHistoryList);
//System.out.println("x="+p.x+"  y="+p.y);
//System.out.println("w="+p.width+"  h="+p.height);
//System.out.println();

			return true;
		}



		/**
		 * 現在、状態が履歴のどの位置にあるのかを示すカウンタ
		 */
		private int mCurrentStateCounter = 0;


		/**
		 * アンドゥ対象オブジェクトの履歴リスト
		 */
		protected ArrayList mUndoableObjectHistoryList = new ArrayList();




		/**
		 * アンドゥを行う
		 */
		public boolean undo()
		{
			this.mStringElementStateCounter--;
			SGProperties p = (SGProperties)this.mStringElementPropertyHistoryList.get(
					this.mStringElementStateCounter);
			if( this.setProperties(p) == false )
			{
				return false;
			}
			return true;
		}


		/**
		 * リドゥを行う
		 */
		public boolean redo()
		{
			this.mStringElementStateCounter++;
			SGProperties p = (SGProperties)this.mStringElementPropertyHistoryList.get(
				this.mStringElementStateCounter);
			if( this.setProperties(p) == false )
			{
				return false;
			}
			return true;
		}



		/**
		 * アンドゥ操作を依頼する
		 */
		public boolean onUndo()
		{
			return this.undo();
		}

	
	
		/**
		 * リドゥ操作を依頼する
		 */
		public boolean onRedo()
		{
			return this.redo();
		}


		/**
		 * オブジェクトの履歴を更新する
		 * @return
		 */
		public boolean updateObjectHistory( final SGIUndoable obj )
		{
			ArrayList objList = new ArrayList();
			objList.add(obj);
			boolean flag = this.updateObjectHistory(objList);
			if( !flag )
			{
				return false;
			}

			return true;
		}



		/**
		 * オブジェクトの履歴を更新する
		 */
		public boolean updateObjectHistory( final ArrayList objList )
		{

			ArrayList list = new ArrayList();
			for( int ii=0; ii<this.mCurrentStateCounter; ii++ )
			{
				Object obj = this.mUndoableObjectHistoryList.get(ii);
				list.add(obj);
			}
			list.add( new ArrayList(objList) );

			this.mUndoableObjectHistoryList = list;
			this.mCurrentStateCounter++;

//System.out.println("<< updateObjectHistory >>");
//System.out.println(this.mUndoableObjectHistoryList);
//System.out.println(this.mCurrentStateCounter);
//System.out.println();

			return true;
		}



		/**
		 * 履歴の更新の実行。アンドゥ対象の操作が行われたときに、真っ先に呼ばれる。
		 */
		public boolean updateHistory()
		{
			if( this.isChanged() )
			{
				this.updateThisObjectHistory();
				this.setChanged(false);
				this.updateObjectHistory(this);
			}
			return true;
		}



		/**
		 * 
		 * @return
		 */
		public boolean updateThisObjectHistory()
		{
			this.mStringElementStateCounter++;
			this.addStringPropertyHistory( this.getProperties() );
			return true;
		}


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

	}

	
	
	
	/**
	 * 
	 */
	public static class LabelProperties extends StringProperties
	{
		
		private float mX = 0.0f;
		private float mY = 0.0f;
		private String mText = null;
		private SGAxis mXAxis = null;
		private SGAxis mYAxis = null;

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


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

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

			LabelProperties p = (LabelProperties)obj;
			
			if( this.mX!=p.mX ) return false;
			if( this.mY!=p.mY ) return false;
			if( this.mText.equals(p.mText) == false ) return false;
			if( p.mXAxis.equals(this.mXAxis) == false ) return false;
			if( p.mYAxis.equals(this.mYAxis) == false ) return false;

			return true;
		}


		public Float getX()
		{
			return new Float( this.mX );
		}

		public Float getY()
		{
			return new Float( this.mY );
		}

		public String getText()
		{
			return this.mText;
		}


		public SGAxis getXAxis()
		{
			return this.mXAxis;
		}
		
		public SGAxis getYAxis()
		{
			return this.mYAxis;
		}


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

		public void setY( final float y )
		{
			this.mY = y;
		}

		
		public void setText( final String text )
		{
			if( text==null )
			{
				throw new IllegalArgumentException("text==null");
			}
			this.mText = text;
		}


		public void setXAxis( SGAxis axis )
		{
			if( axis==null )
			{
				throw new IllegalArgumentException("axis==null");
			}
			this.mXAxis = axis;
		}

		public void setYAxis( SGAxis axis )
		{
			if( axis==null )
			{
				throw new IllegalArgumentException("axis==null");
			}
			this.mYAxis = axis;
		}

	}


	
		
	/**
	 * 
	 */
	public SGProperties getProperties()
	{
		StringElementProperties p = new StringElementProperties();
		p.visibleStringElementList = this.getVisibleStringElementList();
		return p;
	}



	/**
	 * 
	 */
	public boolean setProperties( final SGProperties p )
	{

		if( ( p instanceof StringElementProperties ) == false )
		{
			return false;
		}

		StringElementProperties sp = (StringElementProperties)p;

		this.setVisibleStringElement( sp.visibleStringElementList );

		return true;

	}



	/**
	 * 
	 */
	public static class StringElementProperties extends SGProperties
	{
		ArrayList visibleStringElementList = new ArrayList();

		public StringElementProperties()
		{
			super();
		}

		public boolean equals( final Object obj )
		{

			if( ( obj instanceof StringElementProperties ) == false )
			{
				return false;
			}

			StringElementProperties p = (StringElementProperties)obj;
			
			if( p.visibleStringElementList.equals(this.visibleStringElementList) == false )
			{
				return false;
			}

			return true;

		}

	}



}


