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

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.MediaTracker;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseAdapter;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.print.attribute.standard.MediaSize;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.RootPaneContainer;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;

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


/**
 * The window which has figures in its pane.
 */
public class SGDrawingWindow extends JFrame
	implements ComponentListener, PropertyChangeListener,
		MenuListener, 
		ActionListener,
		SGIUndoable, SGINode, SGIRootObject, SGIWindowDialogObserver,
		SGIProgressControl
{

	/**
	 * 
	 */
	private static final long serialVersionUID = -7587763518020468378L;


	// ID-number of this window
	private int mID;


	// magnification
	private float mMagnification = 1.0f;


	// flag of auto zooming
	private boolean mAutoZoomFlag = false;


	// A layered pane.
	private SGClientPanel mClientPanel;

	
	// Property dialog of this window object
	private SGWindowDialog mDialog = null;


	// image file export manager
	private SGIImageExportManager mImageExportManager;


	// Temporary size of the view port, which is used in the lock mode.
	private final SGTuple2f mTemporaryViewportSize = new SGTuple2f();


	// The tool bar
	private SGToolBar mToolBar;


	// The status bar.
	private SGStatusBar mStatusBar;


	/**
	 * 
	 */
	private boolean mLockFigureFlag = false;


	/**
	 * 
	 */
	private int mMode = MODE_DISPLAY;


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


	/**
	 * 
	 */
	private final SGTuple2f mPaperOrigin = new SGTuple2f();


	/**
	 * Bounds of the client area.
	 */
	private Rectangle2D mClientRect = null;



	/**
	 * Title of this window.
	 */
	public static final String TITLE = "Samurai Graph";



	/**
	 * The list of copied figures.
	 */
	private ArrayList mCopiedFiguresList = new ArrayList();


	/**
	 * The list of copied objects.
	 */

	private ArrayList mCopiedObjectsList = new ArrayList();


	/**
	 * The list of copied objects.
	 */
	private ArrayList mCopiedDataObjectsList = new ArrayList();


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


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


	/**
	 * constructor
	 */
	public SGDrawingWindow()
	{
		super();
		this.setTitle( TITLE );
		this.create();
		
		// set the close operation
		this.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
	}


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



	private static final String[] IMAGE_FILENAMES_ARRAY = {
			SAMURAI_IMG_FILENAME
		};


	/**
	 * Load image objects.
	 * @return a map object
	 */
	private Map loadImages()
	{
		String[] keys = IMAGE_FILENAMES_ARRAY;
		final int num = keys.length;

		ImageIcon[] icons = new ImageIcon[num];
		for( int ii=0; ii<num; ii++ )
		{
			icons[ii] = this.createIcon( keys[ii] );
		}

		MediaTracker mt = new MediaTracker(this);
		HashMap m = new HashMap();
		for( int ii=0; ii<num; ii++ )
		{
			m.put( keys[ii], icons[ii] );
			mt.addImage( icons[ii].getImage(), ii );
		}

		try
		{
			mt.waitForAll();
		}
		catch( InterruptedException e )
		{
			e.printStackTrace();
		}

		return m;
	}




	/**
	 * create window
	 */
	private boolean create()
	{
		// load images		
		Map map = this.loadImages();


		// update the UI
		SwingUtilities.updateComponentTreeUI(this);


		// icon image
		final ImageIcon icon = (ImageIcon)map.get( SAMURAI_IMG_FILENAME );
		this.setIconImage( icon.getImage() );


		// create a property dialog
		this.createPropertyDialog();


		// create the menu bar
		this.createMenuBar();


		// create a tool bar
		this.createToolBar();

		// create a status bar
		this.createStatusBar();

		// create a client panel
		this.createClientPanel();

		// pack
		this.pack();

		// init flag map
		this.initInsertFlagMap();


		//
		// update items
		//

		this.updateItemsByFigureNumbers();
		this.updateGridItems();
		this.updatePaperItems();
		this.updateUndoItems();
		this.updateFocusedObjectItem();
		this.updateZoomItems();
		this.updateSnapToGridItems();
		this.updateModeMenuItems();


		// set saved flag
		this.setSaved(false);

		return true;
	}



	/**
	 * 
	 */
	public void updateItemsByFigureNumbers()
	{
		final boolean b = ( this.getVisibleFigureList().size() !=0 );
		
		// tool bar
		SGToolBar tBar = this.mToolBar;
		tBar.setButtonEnabled( MENUBARCMD_EXPORT_AS_IMAGE, b );
		tBar.setButtonEnabled( MENUBARCMD_SAVE_PROPERTY, b );
		tBar.setButtonEnabled( MENUBARCMD_PRINT, b );
		tBar.setButtonEnabled( MENUBARCMD_BOUNDING_BOX, b );
		tBar.setInsertToggleButtonsEnabled(b);

		// menu bar
		SGMenuBar mBar = this.mMenuBar;
		mBar.setMenuItemEnabled( MENUBAR_FILE, MENUBARCMD_EXPORT_AS_IMAGE, b );
		mBar.setMenuItemEnabled( MENUBAR_FILE, MENUBARCMD_SAVE_PROPERTY, b );
		mBar.setMenuItemEnabled( MENUBAR_FILE, MENUBARCMD_SAVE_DATASET, b );
		mBar.setMenuItemEnabled( MENUBAR_FILE, MENUBARCMD_PRINT, b );
		mBar.setMenuItemEnabled( MENUBAR_LAYOUT, MENUBARCMD_BOUNDING_BOX, b );
		mBar.setMenuItemEnabled( MENUBAR_LAYOUT, MENUBARCMD_SNAP_TO_GRID, b );
		mBar.setMenuItemEnabled( MENUBAR_ARRANGE, MENUBARCMD_AUTO_ARRANGEMENT, b );
		mBar.setInsertToggleButtonsEnabled(b);
	}


	/**
	 * 
	 *
	 */
	private void updateInsertItems()
	{
		String[] array = INSERT_MENUBARCMD_ARRAY;
		for( int ii=0; ii<array.length; ii++ )
		{
			this.updateInsertItems( array[ii] );
		}
	}


	//
	private void updateInsertItems( final String command )
	{
		final boolean b = this.getInsertFlag( command );
		
		SGMenuBar mBar = this.mMenuBar;
		if( mBar.hasMenuItem( command ) )
		{
			mBar.setInsertToggleItemSelected( command, b );
		}
		SGToolBar tBar = this.mToolBar;
		if( tBar.hasButton( command ) )
		{
			tBar.setInsertTogglebuttonSelected( command, b );
		}
	}



	/**
	 * 
	 * @return
	 */
	public boolean init()
	{

		// set default size
		{
			float width = SGIRootObjectConstants.DEFAULT_VIEWPORT_WIDTH;
			float height = width/SGIConstants.GOLDEN_RATIO;
			this.setViewportSize( width, height );
//			this.mLayeredPane.setPreferredSize( new Dimension( (int)width, (int)height ) );
			this.mToolBar.setPreferredSize( new Dimension( (int)width, this.mToolBar.getHeight() ) );
//			this.pack();

			this.mTemporaryViewportSize.setValues( width, height );
		}

		// set the size of the components in window pane
		this.setComponentBounds();


		// set the client rectangle
		this.mClientRect = new Rectangle2D.Float();


		// set the paper rectangle
		final float initX = 0.0f/SGIConstants.CM_POINT_RATIO;
		final float initY = initX;
		this.setPaperOrigin( initX, initY );
		final float width = SGIRootObjectConstants.DEFAULT_PAPER_WIDTH;
		final float height = SGIRootObjectConstants.DEFAULT_PAPER_HEIGHT;
		this.mClientPanel.setPaperSizeRoundingOff( width, height );

		
		//
		this.updateClientRect();


		return true;
	}



	//
	public void repaintContentPane()
	{
		this.getContentPane().repaint();
	}


	/**
	 * name : name of resource file
	 */
	private ImageIcon createIcon( final String name )
	{
		URL url = this.getClass().getResource( RESOURCES_DIRNAME + name );
		ImageIcon icon = new ImageIcon(url);
		return icon;
	}


	// Create the client panel.
	private void createClientPanel()
	{
		SGClientPanel cPanel = new SGClientPanel( this );
		this.getContentPane().add( cPanel, BorderLayout.CENTER );
		this.mClientPanel = cPanel;
	}

	// Create the status bar.
	private void createStatusBar()
	{
		SGStatusBar sBar = new SGStatusBar( this );
		this.getContentPane().add( sBar, BorderLayout.SOUTH );
		this.mStatusBar = sBar;
	}


	/**
	 * 
	 * @return
	 */
	private boolean createToolBar()
	{
		// add components into tool bar
		SGToolBar bar = new SGToolBar();
		this.mToolBar = bar;
		bar.setRoot( this );

		bar.addActionListener( this );
		bar.addPropertyChangeListener( this );

		this.addComponentListener( bar );
		bar.addComponentListener( this );
		this.addComponentListener(this);

		this.setToolBar();

		return true;
	}



	/**
	 * 
	 * @return
	 */
	private boolean setToolBar()
	{

		this.getContentPane().remove( this.mToolBar );


		final int ori = this.mToolBar.getOrientation();
		if( ori == SwingConstants.HORIZONTAL )
		{
			this.getContentPane().add( this.mToolBar, BorderLayout.NORTH );
		}
		else if( ori == SwingConstants.VERTICAL )
		{
			this.getContentPane().add( this.mToolBar, BorderLayout.WEST );
		}

		this.validate();

		return true;
	}



	/**
	 * 
	 */
	private int getToolBarHeight()
	{
		int height = 0;
		if( this.mToolBar.isVisible() )
		{
			height = this.mToolBar.getHeight();
		}
		return height;
	}


	/**
	 * 
	 * @return
	 */
	private int getToolBarWidth()
	{
		int width = 0;
		if( this.mToolBar.isVisible() )
		{
			width = this.mToolBar.getWidth();
		}
		return width;
	}



	/**
	 * Returns an array of keys of inner tool bars.
	 * @return an array of keys of inner tool bars.
	 */
	public String[] getToolBarPattern()
	{
		final String[] pattern = this.mToolBar.getToolBarPattern();
		return pattern;
	}


	/**
	 * Set visible inner tool bars.
	 * @param pattern - an array of keys of visible tool bars.
	 */
	public void setToolBarPattern( final String[] pattern )
	{
		this.mToolBar.setToolBarPattern( pattern );
		this.updateToolBarVisibleMenuItems();
		this.updateToolBarVisibleItems();
	}


	/**
	 * 
	 */
	private boolean createPropertyDialog()
	{
		this.mDialog = new SGWindowDialog(this,true);
		return true;
	}




//
//	set/get methods
//


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



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


	private int mIDCounter = 0;


	/**
	 * 
	 * @return
	 */
	public int getCurrentFigureId()
	{
		this.mIDCounter++;
		return this.mIDCounter;
	}

	
	
	/**
	 * 
	 */
//	private JToolBar mBottomToolBar = new JToolBar();



	/**
	 * 
	 * @return
	 */
	public int getTopWidth()
	{
		final Insets insets = this.getInsets();
		final int iTop = insets.top;

		// width of menu bar
		final JMenuBar menuBar = this.getJMenuBar();
		final int menuBarWidth = menuBar.getHeight();

		// width of tool bar
		int tHeight = 0;
		if( this.mToolBar.getOrientation() == SwingConstants.HORIZONTAL )
		{
			tHeight = this.getToolBarHeight();
		}


		final int width = iTop + menuBarWidth + tHeight;

		return width;
	}



	/**
	 * 
	 * @return
	 */
	public int getBottomWidth()
	{
		final Insets insets = this.getInsets();
		final int iBottom = insets.bottom;

		return iBottom;
	}



	/**
	 * 
	 * @return
	 */
	public int getLeftWidth()
	{
		final Insets insets = this.getInsets();
		final int iLeft = insets.left;

		int tWidth = 0;
		if( this.mToolBar.getOrientation() == SwingConstants.VERTICAL )
		{
			tWidth = this.getToolBarWidth();
		}

		return iLeft + tWidth;
	}



	/**
	 * 
	 * @return
	 */
	public int getRightWidth()
	{
		final Insets insets = this.getInsets();
		final int iRight = insets.right;
		return iRight;
	}



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



	/**
	 * 
	 * @return
	 */
	public float getMagnificationPercent()
	{
		return this.mMagnification*100.0f;
	}



	/**
	 * 
	 * @param creator
	 */
	public void setImageFileCreator( SGIImageExportManager creator )
	{
		this.mImageExportManager = creator;
	}




	//
	// Figures
	//


	/**
	 * The list of figures.
	 */
	private final List mFigureList = new ArrayList();

	
	/**
	 * Returns a figure with given ID.
	 * @param id - Figure ID
	 * @return figure with given ID
	 */
	public SGFigure getFigure( final int id )
	{
		List list = this.mFigureList;
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure f = (SGFigure)list.get(ii);
			if( f.getID() == id )
			{
				return f;
			}
		}
		
		return null;
	}


	/**
	 * Returns the list of figures.
	 * @return a list of figures
	 */
	public ArrayList getFigureList()
	{
		return new ArrayList( this.mFigureList );
	}


	/**
	 * Returns an array of figures.
	 * @return
	 */
	public SGFigure[] getFigureArray()
	{
		return (SGFigure[])this.getFigureList().toArray( new SGFigure[]{} );
	}


	/**
	 * Returns a list of visible figures.
	 * @return a list of visible figures
	 */
	public ArrayList getVisibleFigureList()
	{
		List fList = this.mFigureList;
		ArrayList list = new ArrayList();
		for( int ii=0; ii<fList.size(); ii++ )
		{
			SGFigure f = (SGFigure)fList.get(ii);
			if( f.isVisible() )
			{
				list.add(f);
			}
		}
		return list;
	}


	/**
	 * Hide the figure.
	 * @param figure
	 * @return
	 */
	private boolean hideFigure( SGFigure figure )
	{
		figure.setVisible( false );
		figure.setChanged( true );
		return true;
	}


	/**
	 * Remove all figures.
	 *
	 */
	public void removeAllFigures()
	{
		this.mFigureList.clear();
		this.updateClientRect();
	}


	/**
	 * Remove a figure.
	 * @param figure
	 * @return
	 */
	public boolean removeFigure( final SGFigure figure )
	{
		return this.mFigureList.remove( figure );
	}



	/**
	 * Add a figure to the window.
	 * @param figure
	 * @param pos : mouse location of window
	 * @return
	 */
	public boolean addFigure(
		final SGFigure figure, final Point pos )
	{
		// add to the list
		if( this.addFigure( figure ) == false )
		{
			return false;
		}

		// set location
		Point2D location = this.getLocationInPane(pos.x, pos.y);
		figure.setGraphRectLocation(
			(float)location.getX(), (float)location.getY()
		);

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

		return true;
	}


	/**
	 * Add a new figure.
	 * @param figure - figure to be added.
	 * @return
	 */
	public boolean addFigure( final SGFigure figure )
	{
		// add to the list
		this.mFigureList.add( figure );

		// set the view bounds
		figure.setViewBounds();

		// zoom the added figure
		figure.zoom( this.getMagnification() );

		// update menu items
		this.updateItemsByFigureNumbers();

		return true;
	}
	
	//
	public boolean needsConfirmationBeforeDiscard( )
	{
		return ( this.getVisibleFigureList().size()!=0 & !this.isSaved() );
	}


	/**
	 * 
	 * @param img
	 * @return
	 */
	private boolean _setImage( final Image img )
	{
		boolean changed = false;
		if( img!=null )
		{
			if( img.equals(this.getImage()) == false )
			{
				changed = true;
			}
		}
		else
		{
			if( this.getImage()!=null )
			{
				if( this.getImage().equals(img) == false )
				{
					changed = true;
				}
			}
		}
		
		if( this.mClientPanel.setImage(img) == false )
		{
			return false;
		}
		
		this.setChanged(changed);
		this.notifyToRoot();

		this.repaintContentPane();
		
		return true;
	}


	//
	public boolean setImage( final File f )
	{
		// create an image object
		try
		{
			URL url = f.toURI().toURL();
			Image img = Toolkit.getDefaultToolkit().getImage(url);

			// set the media tracker
			MediaTracker mt = new MediaTracker( this );
			mt.addImage( img, 0 );
			try
			{
				mt.waitForAll();
			}
			catch (InterruptedException ex)
			{

			}

			// add an image to the window
			this._setImage(img);
		}
		catch( MalformedURLException e )
		{
			e.printStackTrace();
			return false;
		}

		return true;
	}

	/**
	 * Delete the background image.
	 * @return
	 */
	public boolean deleteImage()
	{
		return this._setImage( null );
	}


	public boolean setImageLocation( final int x, final int y )
	{
		return this.mClientPanel.setImageLocation(x,y);
	}

	public boolean setImageSize( final int w, final int h )
	{
		return this.mClientPanel.setImageSize(w,h);
	}


	public Image getImage()
	{
		return this.mClientPanel.getImage();
	}


	/**
	 * 
	 * @return
	 */
	public boolean drawBackAllVisibleFigures()
	{
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure fig = (SGFigure)list.get(ii);
			if( fig.drawbackFigure() == false )
			{
				return false;
			}
		}
		return true;
	}

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


	/**
	 * 
	 * @return
	 */
	public boolean clearFocusedObjects()
	{
		ArrayList list = this.getFocusedObjectsList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGISelectable s = (SGISelectable)list.get(ii);
			s.setSelected(false);
		}
		return true;
	}


	/**
	 * 
	 * @return
	 */
	public ArrayList getFocusedObjectsList()
	{
		ArrayList list = new ArrayList();
		this.getFocusedObjectsList(list);
		return list;
	}



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


	/**
	 * 
	 * @return
	 */
	public ArrayList getCopiedFiguresList()
	{
		return new ArrayList( this.mCopiedFiguresList );
	}


	/**
	 * 
	 *
	 */
	public boolean hideSelectedObjects()
	{
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			if( figure.isSelected() )
			{
				this.hideFigure( figure );
			}
			else
			{
				if( figure.hideSelectedObjects() == false )
				{
					return false;
				}
			}
		}
		
		if( list.size()!=0 )
		{
			//
			this.setChanged(true);

			// clear the list
			this.clearFocusedObjects();

			//
			this.updateItemsByFigureNumbers();
		}
		
		return true;
	}



	/**
	 *
	 *
	 */
	protected void setSelectionSymbolsVisible( final boolean b )
	{
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.setSelectionSymbolsVisible(b);
		}
	}

	
	/**
	 * Clear focused figures and focused objects in all figures.
	 */
	public boolean clearAllFocusedObjectsInFigures()
	{
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure fig = (SGFigure)list.get(ii);
			if( fig.clearFocusedObjects() == false )
			{
				return false;
			}
		}
		if( this.clearFocusedFigures() == false )
		{
			return false;
		}
		return true;
	}



	/**
	 * Clear focused figures.
	 * @return true:succeeded, false:failed
	 */
	public boolean clearFocusedFigures()
	{
		ArrayList list = this.getFocusedObjectsList();
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			this.setFocusedFigure( figure, false );
		}
		return true;
	}



	/**
	 * Set the focused figures.
	 * @param figure figure
	 * @param focused flag to set
	 * @return true:succeeded, false:failed
	 */
	public boolean setFocusedFigure( final SGFigure figure, final boolean b )
	{
		figure.setSelected(b);
		figure.setSymbolsVisibleAroundAllObjects(b);
		return true;
	}


//
// components in the window
//


	/**
	 * get view port size
	 */
	public SGTuple2f getViewportSize()
	{
		final SGTuple2f size = this.getPaneSize();
		final int rw = this.mClientPanel.getRulerWidth();
		size.x -= rw;
		size.y -= rw;
		return size;
	}


	/**
	 * 
	 * @return
	 */
	public SGTuple2f getPaneOrigin()
	{
		Rectangle2D rect = this.getPaneBounds();
		final SGTuple2f origin
			= new SGTuple2f( (float)rect.getX(), (float)rect.getY() );
		return origin;
	}


	/**
	 * Returns the size of pane.
	 */
	public SGTuple2f getPaneSize()
	{
		Rectangle2D rect = this.getPaneBounds();
		final SGTuple2f size
			= new SGTuple2f( (float)rect.getWidth(), (float)rect.getHeight() );
		return size;
	}



	public Rectangle2D getPaneBounds()
	{

		// get size of boarder area
		final Insets insets = this.getInsets();
		final int iTop = insets.top;
		final int iBottom = insets.bottom;
		final int iLeft = insets.left;
		final int iRight = insets.right;

		// width of menu bar
		final JMenuBar menuBar = this.getJMenuBar();
		final int menuBarWidth = menuBar.getHeight();

		// width of tool bar
		final int toolBarWidth = this.getToolBarHeight();

		// StatusBar
		final int sbH = this.mStatusBar.getHeight();

		// set size
		final float sizeX = this.getWidth() - ( iLeft + iRight );
		final float sizeY = this.getHeight() - (
			iTop + iBottom + menuBarWidth + toolBarWidth + sbH);

		final float x = iLeft;
		final float y = iTop + menuBarWidth + toolBarWidth;

		Rectangle2D rect = new Rectangle2D.Float(
			x, y, sizeX, sizeY );

		return rect;
	}

	public boolean setPaperSize( final float width, final float height )
	{
		return this.mClientPanel.setPaperSize( width, height );
	}

	/**
	 * 
	 * @return
	 */
	public JComponent getFigurePanel()
	{
		return this.mClientPanel.getFigurePanel();
	}
	
	public float getGridLineInterval()
	{
		return this.mClientPanel.getGridLineInterval();
	}
	
	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean setPaperOrigin( final float x, final float y )
	{
		final Rectangle2D cRect = this.getClientRect();
		final float mag = this.mMagnification;
		final float xx = ( x - (float)cRect.getX() )/mag;
		final float yy = ( y - (float)cRect.getY() )/mag;
		this.mPaperOrigin.setValues( xx, yy );
		return true;
	}


	/**
	 * 
	 */
	private boolean mPaperPortraitFlag = true;


	/**
	 * 
	 * @param b
	 */
	public void setPaperPortrait( final boolean b )
	{
		this.mPaperPortraitFlag = b;
		this.updatePaperItems();
	}


	/**
	 * 
	 * @return
	 */
	public boolean getPaperPortrait()
	{
		return this.mPaperPortraitFlag;
	}

	/**
	 * 
	 * @return
	 */
	public SGTuple2f getPaperSize()
	{
		return new SGTuple2f(this.mClientPanel.getPaperWidth(), this.mClientPanel.getPaperHeight());
	}
	
	
	/**
	 * 
	 * @return
	 */
	public float getPaperX()
	{
		final Rectangle2D cRect = this.getClientRect();
		return (float)cRect.getX() + this.mMagnification*this.mPaperOrigin.x;
	}


	/**
	 * 
	 * @return
	 */
	public float getPaperY()
	{
		final Rectangle2D cRect = this.getClientRect();
		return (float)cRect.getY() + this.mMagnification*this.mPaperOrigin.y;
	}


	/**
	 * 
	 * @return
	 */
	public Rectangle2D getPaperRect()
	{
		final float mag = this.getMagnification();
		Rectangle2D rect = new Rectangle2D.Float(
			this.getPaperX(), 
			this.getPaperY(),
			mag*this.mClientPanel.getPaperWidth(),
			mag*this.mClientPanel.getPaperHeight()
		);
		return rect;
	}


	public static final float PAPER_MARGIN = 2.0f/SGIConstants.CM_POINT_RATIO;


	/**
	 * 
	 * @return
	 */
	public Rectangle2D getBoundingBox()
	{
		Rectangle2D rect = new Rectangle2D.Float();

		final float margin = this.mMagnification*PAPER_MARGIN;
		Rectangle2D pRect = this.getPaperRect();
		rect.setRect(
			pRect.getX(),
			pRect.getY(),
			pRect.getWidth() + margin,
			pRect.getHeight() + margin
		);

		return rect;
	}



	/**
	 * ピクセル単位でビューポートのサイズを指定
	 * 指定されたサイズを元にウインドウの全サイズを決定し、設定する
	 */
	public boolean setViewportSize(
		final float width, final float height )
	{

		this.mTemporaryViewportSize.setValues( width, height );

		// get size of boarder area
		final Insets insets = this.getInsets();
		final int iTop = insets.top;
		final int iBottom = insets.bottom;
		final int iLeft = insets.left;
		final int iRight = insets.right;

		// width of menu bar
		final JMenuBar menuBar = this.getJMenuBar();
		final int menuBarWidth = menuBar.getHeight();

		// width of tool bar
		final int toolBarWidth = this.getToolBarHeight();

		// set size
		final int rw = this.mClientPanel.getRulerWidth();
		final float sizeX = width + iLeft + iRight + rw;
		final float sizeY = height + iTop + iBottom
			+ menuBarWidth + toolBarWidth + rw + this.mStatusBar.getHeight();
		this.setSize( (int)sizeX, (int)sizeY );

		return true;
	}



	/**
	 * 
	 * @return
	 */
	protected boolean setComponentBounds()
	{
		final Rectangle2D rect = this.getPaneBounds();
//		final int x = (int)rect.getX();
//		final int y = (int)rect.getY();
		final int width = (int)rect.getWidth();
		final int height = (int)rect.getHeight();
		this.mClientPanel.setSize ( width, height );

		this.validate();

		return true;
	}



	/**
	 * ウインドウリサイズ時に行われる処理
	 */
	private boolean onResized()
	{
//System.out.println("onResized");
		if( this.getClientRect() == null )
		{
			return false;
		}

		// set the size of the components
		this.setComponentBounds();

		// get and record the size of viewport
		final SGTuple2f size = this.getViewportSize();

		// ratio of the viewport size
		final float ratioX = size.x/this.mTemporaryViewportSize.x;
		final float ratioY = size.y/this.mTemporaryViewportSize.y;

		//
		this.updateClientRect();

		// resize the figures
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.recordFigureRect();
			figure.setViewBounds();
		}

		// when the figures are locked
		if( this.isLocked() )
		{
			// resize the paper
			Rectangle2D pRect = this.getPaperRect();
			final float pWidth = ratioX*(float)pRect.getWidth()/this.mMagnification;
			final float pHeight = ratioY*(float)pRect.getHeight()/this.mMagnification;
			this.mClientPanel.setPaperSizeRoundingOff( pWidth, pHeight );

			// resize the figures
			for( int ii=0; ii<list.size(); ii++ )
			{
				SGFigure figure = (SGFigure)list.get(ii);
				figure.recordFigureRect();
				figure.resize( ratioX, ratioY );
				figure.setChanged(true);
			}

			//
			this.updateClientRect();

			if( this.mTemporaryViewportSize.equals( size ) == false )
			{
				this.setChanged(true);
				this.notifyToRoot();
			}

		}

		//
		this.mTemporaryViewportSize.setValues( size );

		//
		this.zoomWithBoundingBox();

		return true;
	}

	/**
	 * Zoom this object.
	 * @param cl
	 * @return
	 */
	public boolean zoom( final float cl )
	{
		//
		this.mMagnification = cl;

		// zoom figures
		SGFigure[] array = this.getFigureArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii].zoom( this.mMagnification ) == false )
			{
				throw new Error();
			}
		}

		// zoom panels
		this.mClientPanel.setMagnification( cl );

		//
		this.updateClientRect();

		//
//		this.setScrollBarValue();

		this.getContentPane().repaint();


		return true;
	}

	/**
	 * 
	 * @param b
	 */
	public void setAutoZoom( final boolean b )
	{
		this.mAutoZoomFlag = b;
		this.mMenuBar.setMenuItemSelected( MENUBAR_LAYOUT, MENUBARCMD_AUTO_ZOOM, b );
		this.zoomWithBoundingBox();
	}


	/**
	 * 
	 * @return
	 */
	public boolean isAutoZoom()
	{
		return this.mAutoZoomFlag;
	}



	/**
	 * 
	 * @return
	 */
	private boolean zoomWithBoundingBox()
	{
		if( this.isAutoZoom() )
		{
			this.zoomWayOut();
		}

		this.updateZoomItems();

		return true;
	}



	/**
	 * 
	 * @return
	 */
	private boolean zoomWayOut()
	{
		Rectangle2D bbRect = this.getBoundingBox();
		SGTuple2f vpSize = this.getViewportSize();
		final float ratioX = (float)( vpSize.x / ( bbRect.getWidth()/this.mMagnification ) );
		final float ratioY = (float)( vpSize.y / ( bbRect.getHeight()/this.mMagnification ) );
		final float smaller = ( ratioX < ratioY ? ratioX : ratioY );
		final int mag = (int)Math.floor(smaller*100.0f);
		this.setZoomValue( new Integer(mag) );
		return true;
	}



	/**
	 * 
	 * @param mag
	 * @return
	 */
	public boolean setZoomValue( final Number mag )
	{
		this.mToolBar.setZoomValue( mag );
		this.zoom( mag.floatValue()/100.0f );
		this.updateZoomItems();
		return true;
	}


	/**
	 * 
	 * @return
	 */
	private boolean setDefaultZoom()
	{
		return this.setZoomValue( new Integer( DEFAULT_ZOOM ) );
	}


	/**
	 * 
	 */
	public boolean setFigureBoundingBox( final int mode )
	{
		if( mode!=0 && mode!=1 && mode!=2 )
		{
			return false;
		}

		ArrayList rectList = new ArrayList();
		ArrayList fList = this.getVisibleFigureList();
		for( int ii=0; ii<fList.size(); ii++ )
		{
			SGFigure figure = (SGFigure)fList.get(ii);
			rectList.add( figure.getBoundingBox() );
		}
		Rectangle2D bbRect = SGUtility.createUnion( rectList );
		if( bbRect==null )
		{
			return false;
		}

		Rectangle2D cRect = this.getClientRect();
		Rectangle2D pRect = this.getPaperRect();
		float width = (float)pRect.getWidth();
		float height = (float)pRect.getHeight();
		final float mag = this.mMagnification;

		// width
		if( mode==0 | mode==1 )
		{
			width = BOUNDING_BOX_MARGIN + (float)( - cRect.getX() + bbRect.getX() + bbRect.getWidth() )/mag;
		}

		// height
		if( mode==0 | mode==2 )
		{
			height = BOUNDING_BOX_MARGIN + (float)( - cRect.getY() + bbRect.getY() + bbRect.getHeight() )/mag;
		}

		// set to the paper
		this.mClientPanel.setPaperSizeRoundingOut( width, height );

		this.updateClientRect();

		return true;
	}



	/**
	 * 引数で渡された全てのフィギュアを外包するような矩形を返す。
	 */
	public Rectangle2D getBoundingBoxOfFigures( final ArrayList figureList )
	{

		if( figureList==null )
		{
			return null;
		}

		if( figureList.size() == 0 )
		{
			return new Rectangle2D.Float();
		}

		ArrayList list = new ArrayList();
		for( int ii=0; ii<figureList.size(); ii++ )
		{
			final SGFigure figure = (SGFigure)figureList.get(ii);
			if( figure==null )
			{
				continue;
			}

			Rectangle2D rect = figure.getBoundingBox();
			if( rect==null )
			{
				return null;
			}
			list.add( rect );
		}

		Rectangle2D rectAll = SGUtility.createUnion(list);


		return rectAll;

	}

	/**
	 * 
	 */
	public Object getComponent( final int x, final int y )
	{
		Object com = this;

		SGFigure[] array = this.getFigureArray();
		for( int ii=array.length-1; ii>=0; ii-- )
		{
			if( array[ii].isVisible() == false )
			{
				continue;
			}

			Rectangle2D rect = array[ii].getGraphRect();
			Point2D p = this.getLocationInPane(x,y);
			if( rect.contains(p) )
			{
				com = array[ii];
				break;
			}
		}

		return com;
	}



	private boolean setDirectlyBefore()
	{
		return this.prepare();
	}

	private boolean setDirectlyAfter()
	{
		if( this.commit() == false )
		{
			return false;
		}
		this.notifyToRoot();
		this.repaintContentPane();
		return true;
	}


	public boolean setPaperWidthDirectly( final float w, final String unit )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setPaperWidth( w, unit ) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}

	public boolean setPaperHeightDirectly( final float h, final String unit )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setPaperHeight( h, unit ) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}

	public boolean setPaperSizeDirectly(
		final float w, final String unitw, final float h, final String unith )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setPaperWidth( w, unitw ) == false ) return false;
		if( this.mClientPanel.setPaperHeight( h, unith ) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}

	public boolean setGridLineWidthDirectly( final float value, final String unit )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setGridLineWidth( value, unit ) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}

	public boolean setGridLineIntervalDirectly( final float value, final String unit )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setGridLineInterval( value, unit ) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}

	public boolean setGridLineVisibleDirectly( final boolean b )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setGridLineVisible(b) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}

	public boolean setGridLineColorDirectly( final Color cl )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setGridLineColor(cl) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}

	public boolean setPaperColorDirectly( final Color cl )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setPaperColor(cl) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}

	public boolean setImageLocationXDirectly( final float x, final String unit )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setImageLocationX( x, unit ) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}

	public boolean setImageLocationYDirectly( final float y, final String unit )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setImageLocationY( y, unit ) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}

	public boolean setImageScalingFactorDirectly( final float f )
	{
		if( this.setDirectlyBefore() == false ) return false;
		if( this.mClientPanel.setImageScalingFactor(f) == false ) return false;
		if( this.setDirectlyAfter() == false ) return false;
		return true;
	}


//
// イベント関係
//

	public void propertyChange( PropertyChangeEvent e )
	{
//System.out.println(e);

		Object source = e.getSource();
//		String pName = e.getPropertyName();
//		Object oldValue = e.getOldValue();
//		Object newValue = e.getNewValue();

		this.onResized();

		if( source.equals( this.mToolBar ) )
		{
			this.updateToolBarVisibleMenuItems();
			
			this.firePropertyChange( PROPERTY_NAME_TOOL_BAR, null, null );
		}

	}



	/**
	 * 
	 */
	public void menuSelected( MenuEvent e )
	{
		this.createPropertyMenuBarItem();
	}


	public void menuDeselected( MenuEvent e ){}

	public void menuCanceled( MenuEvent e ){}



	/**
	 * コンポーネントが可視になると呼び出されます。 
	 */
	public void componentShown(final ComponentEvent e)
	{
	}

	/**
	 * コンポーネントが不可視になると呼び出されます。 
	 */
	public void componentHidden(final ComponentEvent e){}

	/**
	 * コンポーネントの位置が変わると呼び出されます。 
	 */
	public void componentMoved(final ComponentEvent e){}


	/**
	 * コンポーネントのサイズが変わると呼び出されます。 
	 */
	public void componentResized(final ComponentEvent e)
	{
		this.onResized();
	}



//	private int getTopShift()
//	{
//		return this.getTopWidth() + this.mClientPanel.getRulerWidth();
//	}


//	private int getLeftShift()
//	{
//		return this.getLeftWidth() + this.mClientPanel.getRulerWidth();
//	}


	/**
	 * 
	 */
	protected boolean showPropertyDialog()
	{
		final SGPropertyDialog dg = this.mDialog;

		// add an observer
		dg.addPropertyDialogObserver(this);

		// set the location
		dg.setLocation( this.getLocation() );

		// ダイアログに設定
		this.setDialogProperty();

		// 色選択ボタンに装飾
		dg.setColorButtonBorder( true );

		// create temporary objects
		this.prepare();

		// show the dialog
		dg.setVisible(true);

		// remove an observer
		dg.removePropertyDialogObserver(this);

		return true;
	}
	

	// insert a symbol to figure
	protected boolean insertSymbol( final SGFigure figure, final int x, final int y )
	{

		boolean flag = false;
		
		// a label
		if( this.getLabelInsertionFlag() )
		{
			flag = figure.addString(x,y);
		}

		// a timing line
		if( this.getTimingLineInsertionFlag() )
		{
			flag = figure.addTimingLine(x,y);
		}

		// an axis break symbol
		if( this.getAxisBreakSymbolInsertionFlag() )
		{
			flag = figure.addAxisBreakSymbol(x,y);
		}

		// a symbol of significant difference
		if( this.getSignificantDifferenceSymbolInsertionFlag() )
		{
			flag = figure.addSignificantDifferenceSymbol(x,y);
		}

		// rectangle
		if( this.getRectangleInsertionFlag() )
		{
			flag = figure.addShape( SGIFigureElementShape.RECTANGLE, x, y );
		}

		// ellipse
		if( this.getEllipseInsertionFlag() )
		{
			flag = figure.addShape( SGIFigureElementShape.ELLIPSE, x, y );
		}

		// arrow
		if( this.getArrowInsertionFlag() )
		{
			flag = figure.addShape( SGIFigureElementShape.ARROW, x, y );
		}

		// line
		if( this.getLineInsertionFlag() )
		{
			flag = figure.addShape( SGIFigureElementShape.LINE, x, y );
		}

		return flag;
	}


	protected final Point mTempMouseLocation = new Point();


	/**
	 * 
	 * @param f
	 */
	void clearFocusedFiguresOtherThan( SGFigure f )
	{
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			final SGFigure figure = (SGFigure)list.get(ii);
			if( !figure.equals(f) )
			{
				figure.setSelected(false);
			}
		}
	}


	/**
	 * Translate all selected objects.
	 * 
	 * @param dx
	 * @param dy
	 * @return
	 */
	public boolean translateFocusedObjects( final int dx, final int dy )
	{
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			final SGFigure figure = (SGFigure)list.get(ii);
			if( figure.isSelected() )
			{
				figure.translate( dx, dy );
			}
			else
			{
				figure.translateSelectedObjects(dx,dy);
			}
		}

		
		return true;
	}



	/**
	 * 
	 * @param x	New X Coordinate
	 * @param y	New Y Coordinate
	 * @return
	 */
	protected boolean moveFocusedObjects( final int x, final int y )
	{
		final int dx = x - this.mTempMouseLocation.x;
		final int dy = y - this.mTempMouseLocation.y;

		this.translateFocusedObjects( dx, dy );

		// update the pressed point
		this.mTempMouseLocation.setLocation(
			this.mTempMouseLocation.x + dx,
			this.mTempMouseLocation.y + dy
		);

		return true;
	}


	/**
	 * 
	 */
	private Point2D getLocationInPane( final int x, final int y )
	{
		
		int xx = x;
		int yy = y;


		// get size of boarder area
		final Insets insets = this.getInsets();
		final int mTop = insets.top;
//		final int mBottom = insets.bottom;
		final int mLeft = insets.left;
//		final int mRight = insets.right;

		xx -= mLeft;
		yy -= mTop;

		// menu bar
		final JMenuBar menuBar = this.getJMenuBar();
		final double menuHeight = menuBar.getHeight();
		yy -= menuHeight;

		// tool bar
		yy -= this.getToolBarHeight();

		// ruler
		final double rulerWidth = this.mClientPanel.getRulerWidth();
		xx -= rulerWidth;
		yy -= rulerWidth;

		return new Point2D.Float(xx, yy);
	}


	/**
	 * 位置情報ラベルへの設定。
	 */
	public boolean setPositionLabel( final int x, final int y )
	{
		final Rectangle2D cRect = this.getClientRect();
		final float cx = (float)cRect.getX();
		final float cy = (float)cRect.getY();

		final float ratio = CM_POINT_RATIO/this.mMagnification;
		float xx = (-cx+x)*ratio;
		float yy = (-cy+y)*ratio;

		this.mStatusBar.drawPosition( xx, yy );

		return true;
	}



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



	/**
	 * 
	 */
	public void addActionListener( final ActionListener listener )
	{
		ArrayList list = this.mActionListenerList;
		for( int ii=0; ii<list.size(); ii++ )
		{
			final ActionListener el = (ActionListener)list.get(ii);
			if( el.equals(listener) )
			{
				return;
			}
		}
		list.add(listener);
	}



	/**
	 * 
	 */
	public void removeActionListener( ActionListener listener )
	{
		ArrayList list = this.mActionListenerList;
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final ActionListener el = (ActionListener)list.get(ii);
			if( el.equals(listener) )
			{
				this.mActionListenerList.remove(listener);
			}
		}
	}



	/**
	 * 
	 */
	public void notifyToListener( final String command )
	{
		ArrayList list = this.mActionListenerList;
		for( int ii=0; ii<list.size(); ii++ )
		{
			final ActionListener el = (ActionListener)list.get(ii);
			el.actionPerformed( this.getActionEvent( command ) );
		}
	}



	/**
	 * 
	 */
	private ActionEvent getActionEvent( final String command )
	{
		return new ActionEvent( this, 0, command );
	}




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



	/**
	 * 
	 * @param flag
	 * @return
	 */
	protected void setInsertToggleItemsUnselected()
	{
		this.mMenuBar.setInsertToggleItemsUnSelected();
		this.mToolBar.setInsertToggleItemsUnSelected();

		Map map = this.mInsertFlagMap;
		Boolean b = Boolean.FALSE;

		String[] cmdArray = INSERT_MENUBARCMD_ARRAY;
		for( int ii=0; ii<cmdArray.length; ii++ )
		{
			map.put( cmdArray[ii], b );
		}

		this.updateInsertItems();
	}


	private void initInsertFlagMap()
	{
		Map map = new HashMap();
		Boolean b = Boolean.FALSE;

		String[] cmdArray = INSERT_MENUBARCMD_ARRAY;
		for( int ii=0; ii<cmdArray.length; ii++ )
		{
			map.put( cmdArray[ii], b );
		}

		this.mInsertFlagMap = map;
	}

	private Map mInsertFlagMap;


	/**
	 * 
	 * @return
	 */
	public boolean isInsertFlagSelected()
	{
		Map map = this.mInsertFlagMap;
		Iterator itr = map.values().iterator();
		while( itr.hasNext() )
		{
			Boolean b = (Boolean)itr.next();
			if( b.booleanValue() )
			{
				return true;
			}
		}
		return false;
	}


	/**
	 * 
	 * @param command
	 * @return
	 */
	public boolean getInsertFlag( final String command )
	{
		Object obj = this.mInsertFlagMap.get(command);
		if( obj==null )
		{
			throw new IllegalArgumentException();
		}
		Boolean b = (Boolean)obj;
		return b.booleanValue();
	}


	/**
	 * 
	 * @param command
	 * @param b
	 */
	public void setInsertFlag( final String command, final boolean b )
	{
		this.mInsertFlagMap.put( command, Boolean.valueOf(b) );
	}


	/**
	 * 
	 * @return
	 */
	public boolean getLabelInsertionFlag()
	{
		return this.getInsertFlag( MENUBARCMD_INSERT_LABEL );
	}
	

	/**
	 * 
	 * @return
	 */
	public boolean getTimingLineInsertionFlag()
	{
		return this.getInsertFlag( MENUBARCMD_INSERT_TIMING_LINE );
	}

	
	/**
	 * 
	 * @return
	 */
	public boolean getAxisBreakSymbolInsertionFlag()
	{
		return this.getInsertFlag( MENUBARCMD_INSERT_AXIS_BREAK_SYMBOL );
	}

	
	/**
	 * 
	 * @return
	 */
	public boolean getSignificantDifferenceSymbolInsertionFlag()
	{
		return this.getInsertFlag( MENUBARCMD_INSERT_SIG_DIFF_SYMBOL );
	}


	/**
	 * 
	 * @return
	 */
	public boolean getRectangleInsertionFlag()
	{
		return this.getInsertFlag( MENUBARCMD_INSERT_RECTANGLE );
	}


	/**
	 * 
	 * @return
	 */
	public boolean getEllipseInsertionFlag()
	{
		return this.getInsertFlag( MENUBARCMD_INSERT_ELLIPSE );
	}


	/**
	 * 
	 * @return
	 */
	public boolean getArrowInsertionFlag()
	{
		return this.getInsertFlag( MENUBARCMD_INSERT_ARROW );
	}


	/**
	 * 
	 * @return
	 */
	public boolean getLineInsertionFlag()
	{
		return this.getInsertFlag( MENUBARCMD_INSERT_LINE );
	}



	/**
	 * Paste the objects to the target figures.
	 * @param list - a list of the objects.
	 * @param dataList - a list of a data objects.
	 * @param nameList - a list of a data name.
	 */
	public void pasteToFigures(
		ArrayList list,
		ArrayList dataList,
		ArrayList nameList,
		ArrayList propertiesMapList )
	{
		ArrayList fList = this.getFocusedObjectsList();
		if( fList.size()==0 )
		{
			return;
		}

		// paste to the target object
		for( int ii=0; ii<fList.size(); ii++ )
		{
			SGFigure figure = (SGFigure)fList.get(ii);
			figure.paste( list );
			for( int jj=0; jj<nameList.size(); jj++ )
			{
				SGData data = (SGData)dataList.get(jj);
				String name = (String)nameList.get(jj);
				Map map = (Map)propertiesMapList.get(jj);
				SGData dataNew = (SGData)data.copy();
				figure.addData( dataNew, name, map );
			}
		}


		// repaint after pasted
		this.repaintContentPane();


		// notify the change to the root
		this.notifyToRoot();

	}

	/**
	 * 
	 *
	 */
	protected void updateDataItem()
	{
		this.updateFocusedObjectItem();
	}



	/**
	 * 
	 * @return
	 */
	protected void updateFocusedObjectItem()
	{
		boolean eff = false;
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			if( figure.isSelected() )
			{
				eff = true;
				break;
			}
			
			SGIFigureElement[] array = figure.getIFigureElementArray();
			for( int jj=0; jj<array.length; jj++ )
			{
				if( array[jj].getFocusedObjectsList().size()!=0 )
				{
					eff = true;
					break;
				}
			}
			
			if( eff )
			{
				break;
			}
		}
		
		
		// set to the menu bar
		SGMenuBar mBar = this.mMenuBar;
		mBar.setMenuItemEnabled( MENUBAR_EDIT, MENUBARCMD_CUT, eff );
		mBar.setMenuItemEnabled( MENUBAR_EDIT, MENUBARCMD_COPY, eff );
		mBar.setMenuItemEnabled( MENUBAR_EDIT, MENUBARCMD_DELETE, eff );
		mBar.setMenuItemEnabled( MENUBAR_EDIT, MENUBARCMD_DUPLICATE, eff );
		mBar.setMenuItemEnabled( MENUBAR_ARRANGE, MENUBARCMD_MOVE_TO_FRONT, eff );
		mBar.setMenuItemEnabled( MENUBAR_ARRANGE, MENUBARCMD_MOVE_TO_BACK, eff );


		// set to the tool bar
		SGToolBar tBar = this.mToolBar;
		tBar.setButtonEnabled( MENUBARCMD_CUT, eff );
		tBar.setButtonEnabled( MENUBARCMD_COPY, eff );

	}
	
	
	/**
	 * 
	 * @param b
	 */
	public void setPasteMenuEnabled( final boolean b )
	{
		this.mMenuBar.setMenuItemEnabled( MENUBAR_EDIT, MENUBARCMD_PASTE, b );

		this.mToolBar.setButtonEnabled( MENUBARCMD_PASTE, b );
		
		this.mClientPanel.setPopupMenuEnabled( MENUBARCMD_PASTE, b );
	}
	
	
	/**
	 * 
	 *
	 */
	private void updateGridItems()
	{
		final boolean gridVisible = this.mClientPanel.isGridLineVisible();
		boolean plusFlag = false;
		boolean minusFlag = false;
		if( gridVisible )
		{
			final double interval = this.mClientPanel.getGridLineInterval()*CM_POINT_RATIO;
			final double min = SGIRootObjectConstants.GRID_INTERVAL_MIN_VALUE;
			final double max = SGIRootObjectConstants.GRID_INTERVAL_MAX_VALUE;
			final double step = SGIRootObjectConstants.GRID_INTERVAL_STEP_SIZE;
			final int nCeilInterval = (int)Math.ceil(interval/step);
			final int nFloorInterval = (int)Math.floor(interval/step);
			final int nMin = (int)Math.rint(min/step);
			final int nMax = (int)Math.rint(max/step);
			if( nMin < nFloorInterval )
			{
				minusFlag = true;
			}
			if( nCeilInterval < nMax )
			{
				plusFlag = true;
			}
		}


		// set to the menu bar
		SGMenuBar bar = this.mMenuBar;
		bar.setMenuItemSelected( MENUBAR_LAYOUT, MENUBARCMD_GRID_VISIBLE, gridVisible );
		bar.setMenuItemEnabled( MENUBAR_LAYOUT, MENUBARCMD_PLUS_GRID, plusFlag );
		bar.setMenuItemEnabled( MENUBAR_LAYOUT, MENUBARCMD_MINUS_GRID, minusFlag );
	}


	//
	private void updateModeMenuItems()
	{
//		final boolean b = ( this.getMode() == MODE_NORMAL );
//		this.mMenuBar.setMenuItemSelected(
//			MENUBAR_ARRANGE, MENUBARCMD_MODE, b );
	}


	//
	void updateSnapToGridItems()
	{
		this.mMenuBar.setMenuItemSelected(
			MENUBAR_LAYOUT, MENUBARCMD_SNAP_TO_GRID, SGFigure.isSnappingToGrid() );
	}


	//
	private void updateZoomItems()
	{
		final int[] array = SGIRootObjectConstants.MAGNIFICATION_ARRAY;
		final int max = array[0];
		final int min = array[array.length-1];
		final int mag = (int)( this.getMagnificationPercent() );

		boolean zoomIn;
		boolean zoomOut;
		boolean def;
		boolean zoomWayOut;

		// set to the menu bar
		if( this.isAutoZoom() )
		{
			zoomIn = false;
			zoomOut = false;
			def = false;
			zoomWayOut = false;
		}
		else
		{
			zoomIn = (mag!=max);
			zoomOut = (mag!=min);
			def = (mag!=DEFAULT_ZOOM);
			zoomWayOut = true;
		}

		SGMenuBar bar = this.mMenuBar;
		bar.setMenuItemEnabled( MENUBAR_LAYOUT, MENUBARCMD_ZOOM_IN, zoomIn );
		bar.setMenuItemEnabled( MENUBAR_LAYOUT, MENUBARCMD_ZOOM_OUT, zoomOut );
		bar.setMenuItemEnabled( MENUBAR_LAYOUT, MENUBARCMD_DEFAULT_ZOOM, def );
		bar.setMenuItemEnabled( MENUBAR_LAYOUT, MENUBARCMD_ZOOM_WAY_OUT, zoomWayOut );
	}


	//
	private void updateToolBarVisibleItems()
	{
		String[] keys = TOOLBAR_MENUCMD_ARRAY;
		SGToolBar tBar = this.mToolBar;
		SGMenuBar mBar = this.mMenuBar;
		for( int ii=0; ii<keys.length; ii++ )
		{
			tBar.setToolBarVisible( keys[ii], mBar.isToolBarMenuSelected( keys[ii] ) );
		}

	}

	//
	private void updateToolBarVisibleMenuItems()
	{
		String[] keys = TOOLBAR_MENUCMD_ARRAY;
		SGToolBar tBar = this.mToolBar;
		SGMenuBar mBar = this.mMenuBar;
		for( int ii=0; ii<keys.length; ii++ )
		{
			mBar.setToolBarMenuItemSelected( keys[ii], tBar.isToolBarVisible( keys[ii] ) );
		}
	}



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

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

//System.out.println(source);
//System.out.println(command);
//System.out.println();

		//
		// menu bar
		//

		if( command.equals(MENUBARCMD_EXIT) )
		{
			this.notifyToListener( MENUBARCMD_EXIT );
		}
		else if( command.equals(MENUBARCMD_CREATE_NEW_WINDOW) )
		{
			this.notifyToListener( MENUBARCMD_CREATE_NEW_WINDOW );
		}
		else if( command.equals(MENUBARCMD_CLOSE_WINDOW) )
		{
			this.notifyToListener( MENUBARCMD_CLOSE_WINDOW );
		}
		else if( command.equals(MENUBARCMD_DRAW_GRAPH) )
		{
			this.notifyToListener( MENUBARCMD_DRAW_GRAPH );
		}
		else if( command.equals(MENUBARCMD_LOAD_PROPERTY) )
		{
			this.notifyToListener( MENUBARCMD_LOAD_PROPERTY );
		}
		else if( command.equals(MENUBARCMD_SAVE_PROPERTY) )
		{
			this.mPropertyFileCreationModeOfFigures = ALL_FIGURES;
			this.notifyToListener( MENUBARCMD_SAVE_PROPERTY );
		}
		else if( command.equals(MENUBARCMD_LOAD_DATASET) )
		{
			this.notifyToListener( MENUBARCMD_LOAD_DATASET );
		}
		else if( command.equals(MENUBARCMD_SAVE_DATASET) )
		{
			this.mPropertyFileCreationModeOfFigures = ALL_FIGURES;
			this.notifyToListener( MENUBARCMD_SAVE_DATASET );
		}
		else if( command.equals(MENUBARCMD_LOAD_BACKGROUND_IMAGE) )
		{
			this.notifyToListener( MENUBARCMD_LOAD_BACKGROUND_IMAGE );
		}
		else if( command.equals(MENUBARCMD_EXPORT_AS_IMAGE) )
		{
			this.notifyToListener( MENUBARCMD_EXPORT_AS_IMAGE );
		}
		else if( command.equals(MENUBARCMD_PRINT) )
		{
			this.notifyToListener( MENUBARCMD_PRINT );
		}
		else if( command.equals( MENUBARCMD_DELETE ) )
		{
			this.deleteFocusedObjects();
		}
		else if( command.equals( MENUBARCMD_CUT ) )
		{
			this.cutFocusedObjects();
		}
		else if( command.equals( MENUBARCMD_COPY ) )
		{
			this.copyFocusedObjects();
		}
		if( command.equals( MENUBARCMD_PASTE ) )
		{
			this.pasteCopiedObjects();
		}
		else if( command.equals( MENUBARCMD_DUPLICATE ) )
		{
			this.duplicateFocusedObjects();
		}
		else if( command.equals( MENUBARCMD_DELETE_BACKGROUND_IMAGE ) )
		{
			this.deleteImage();
		}
		else if( command.equals( MENUBARCMD_MOVE_TO_FRONT ) )
		{
			this.moveFocusedObjectsToFront();
		}
		else if( command.equals( MENUBARCMD_MOVE_TO_BACK ) )
		{
			this.moveFocusedObjectsToBack();
		}
		else if( command.equals( MENUBARCMD_UNDO ) )
		{
			this.undo();
			this.repaintContentPane();
		}
		else if( command.equals( MENUBARCMD_REDO ) )
		{
			this.redo();
			this.repaintContentPane();
		}
		else if( command.equals( MENUBARCMD_CLEAR_UNDO_BUFFER ) )
		{
			this.clearUndoBuffer();
		}
		else if( command.equals( MENUBARCMD_PAPER_A4_SIZE )
			|| command.equals( MENUBARCMD_PAPER_B5_SIZE )
			|| command.equals( MENUBARCMD_PAPER_USLETTER_SIZE ) )
		{
			MediaSize size = SGUtilityText.getMediaSize( command );
			if( size==null )
			{
				return;
			}
			this.setPaperSizeDirectly( size );
		}
		else if( command.equals( MENUBARCMD_PAPER_PORTRAIT )
			|| command.equals( MENUBARCMD_PAPER_LANDSCAPE ) )
		{
			final boolean b = ( command.equals(MENUBARCMD_PAPER_PORTRAIT) );
			this.setPaperPortrait(b);
		}
		else if( command.equals( MENUBARCMD_BOUNDING_BOX ) )
		{
			this.setBoundingBox();
		}
		else if( command.equals( MENUBARCMD_PAPER_USER_CUSTOMIZE ) )
		{
			this.showPropertyDialog();
		}
		else if( command.equals( MENUBARCMD_MODE ) )
		{
			final int mode = (this.getMode()==MODE_EXPORT_AS_IMAGE) ? MODE_DISPLAY : MODE_EXPORT_AS_IMAGE;
			this.setMode( mode );
			this.updateModeMenuItems();
		}
		else if( command.equals( MENUBARCMD_AUTO_ARRANGEMENT ) )
		{
			this.alignFigures();
		}
		else if( command.equals( MENUBARCMD_GRID_VISIBLE ) )
		{
			this.mClientPanel.setGridLineVisible( !this.mClientPanel.isGridLineVisible() );
			this.updateGridItems();

			this.setChanged(true);
			this.notifyToRoot();
			this.repaintContentPane();
		}
		else if( command.equals( MENUBARCMD_PLUS_GRID ) )
		{
			final double value = this.mClientPanel.getGridLineInterval()*CM_POINT_RATIO;
			final double min = SGIRootObjectConstants.GRID_INTERVAL_MIN_VALUE;
			final double max = SGIRootObjectConstants.GRID_INTERVAL_MAX_VALUE;
			final double step = SGIRootObjectConstants.GRID_INTERVAL_STEP_SIZE;
			double valueNew = SGUtilityNumber.stepValue(
				true, value, min, max, step, 0.001f );
			final int indexNew = (int)Math.rint(valueNew/step);
			final int indexMax = (int)Math.rint(max/step);
			if( indexNew!=indexMax+1 )
			{
				if( valueNew > max )
				{
					valueNew = max;
				}
				this.mClientPanel.setGridLineInterval( (float)valueNew/SGIConstants.CM_POINT_RATIO );
				this.updateGridItems();
				this.getContentPane().repaint();

				this.setChanged(true);
				this.notifyToRoot();
			}
		}
		else if( command.equals( MENUBARCMD_MINUS_GRID ) )
		{
			final double value = this.mClientPanel.getGridLineInterval()*CM_POINT_RATIO;
			final double min = SGIRootObjectConstants.GRID_INTERVAL_MIN_VALUE;
			final double max = SGIRootObjectConstants.GRID_INTERVAL_MAX_VALUE;
			final double step = SGIRootObjectConstants.GRID_INTERVAL_STEP_SIZE;
			double valueNew = SGUtilityNumber.stepValue(
				false, value, min, max, step, 0.001f );
			final int indexNew = (int)Math.rint(valueNew/step);
			final int indexMin = (int)Math.rint(min/step);
			if( indexNew!=indexMin-1 )
			{
				if( valueNew < min )
				{
					valueNew = min;
				}
				this.mClientPanel.setGridLineInterval( (float)valueNew/SGIConstants.CM_POINT_RATIO );
				this.updateGridItems();
				this.getContentPane().repaint();

				this.setChanged(true);
				this.notifyToRoot();
			}
		}
		else if( command.equals( MENUBARCMD_SNAP_TO_GRID ) )
		{
			SGFigure.setSnappingToGrid( !SGFigure.isSnappingToGrid() );
			this.updateSnapToGridItems();
		}
		else if( command.equals( MENUBARCMD_ZOOM_IN ) )
		{
			final int mag = (int)( this.getMagnificationPercent() );
			final int[] array = SGIRootObjectConstants.MAGNIFICATION_ARRAY;
			for( int ii=array.length-1; ii>=0; ii-- )
			{
				if( array[ii] > mag )
				{
					this.setZoomValue( new Integer( array[ii] ) );
					break;
				}
			}
		}
		else if( command.equals( MENUBARCMD_ZOOM_OUT ) )
		{
			final int mag = (int)( this.getMagnificationPercent() );
			final int[] array = SGIRootObjectConstants.MAGNIFICATION_ARRAY;
			for( int ii=0; ii<array.length; ii++ )
			{
				if( array[ii] < mag )
				{
					this.setZoomValue( new Integer( array[ii] ) );
					break;
				}
			}
		}
		else if( command.equals( MENUBARCMD_DEFAULT_ZOOM ) )
		{
			this.setDefaultZoom();
		}
		else if( command.equals( MENUBARCMD_ZOOM_WAY_OUT ) )
		{
			this.zoomWayOut();
		}
		else if( command.equals( MENUBARCMD_AUTO_ZOOM ) )
		{
			this.setAutoZoom( !this.isAutoZoom() );
		}
		else if( command.equals( MENUBARCMD_LOCK ) )
		{
			this.setLocked( !this.isLocked() );
		}
		else if( command.equals( MENUBARCMD_PROPERTIES_WINDOW ) )
		{
			this.createPropertyMenuBarItem();
		}
		else if( command.equals( MENUBARCMD_UPGRADE ) )
		{
			this.notifyToListener( MENUBARCMD_UPGRADE );
		}
		else if( command.equals( MENUBARCMD_CHANGE_LOG ) )
		{
			this.notifyToListener( MENUBARCMD_CHANGE_LOG );
		}
		else if( command.equals( MENUBARCMD_PROXY ) )
		{
			this.notifyToListener( MENUBARCMD_PROXY );
		}
		else if( command.equals( MENUBARCMD_ABOUT ) )
		{
			this.notifyToListener( MENUBARCMD_ABOUT );
		}
//		else if( command.equals( MENUBARCMD_LAF_METAL ) )
//		{
//			this.setLookAndFeel( LAF_CLASS_NAME_METAL );
//		}
//		else if( command.equals( MENUBARCMD_LAF_MOTIF ) )
//		{
//			this.setLookAndFeel( LAF_CLASS_NAME_MOTIF );
//		}
//		else if( command.equals( MENUBARCMD_LAF_WINDOWS ) )
//		{
//			this.setLookAndFeel( LAF_CLASS_NAME_WINDOWS );
//		}
//		else if( command.equals( MENUBARCMD_LAF_WINDOWSCLASSIC ) )
//		{
//			this.setLookAndFeel( LAF_CLASS_NAME_WINDOWSCLASSIC );
//		}
//		else if( command.equals( MENUBARCMD_LAF_AQUA ) )
//		{
//			this.setLookAndFeel( LAF_CLASS_NAME_AQUA );
//		}


		// menu to insert a symbol
		if( Arrays.asList( INSERT_MENUBARCMD_ARRAY ).contains( command ) )
		{

			boolean selected;

			// synchronize the tool bar and the menu bar
			if( source.equals( this.mMenuBar ) )
			{
				selected = this.mMenuBar.isInsertToggleItemSelected( command );
			}
			else if( source.equals( this.mToolBar ) )
			{
				selected = this.mToolBar.isInsertTogglebuttonSelected( command );
			}
			else
			{
				return;
			}

			this.setInsertToggleItemsUnselected();
			this.setInsertFlag( command, selected );
			this.updateInsertItems();


			// change the mouse cursor
			if( selected )
			{
				final Cursor cur = new Cursor( Cursor.CROSSHAIR_CURSOR );
				this.setCursor( cur );
			}
			else
			{
				this.setCursor( null );
			}
		}


		// menu for the tool bar
		if( Arrays.asList( TOOLBAR_MENUCMD_ARRAY ).contains( command ) )
		{
			if( source.equals( this.mMenuBar ) )
			{
				this.updateToolBarVisibleItems();
			}
			else if( source.equals( this.mToolBar ) )
			{
				this.updateToolBarVisibleMenuItems();
			}

//System.out.println(command);
//System.out.println(source);
//System.out.println();
			this.firePropertyChange( PROPERTY_NAME_TOOL_BAR, null, null );
		}

//
//		//
//		// Pop-up menu
//		//
//		
//		if( command.equals( MENUCMD_PROPERTY ) )
//		{
//			this.showPropertyDialog();
//		}

	}



	public void setMode( final int mode )
	{
		this.mMode = mode;
		SGFigure[] array = this.getFigureArray();
		for( int ii=0; ii<array.length; ii++ )
		{
			array[ii].setMode( mode );
		}
	}

	public int getMode()
	{
		return this.mMode;
	}

	public int exportAsImage( final Properties p, final String type, final String path )
	{
	    this.startExport();

	    Component target = this.getExportTarget();

	    if( this.mImageExportManager.export(
	            target,
	            target.getWidth(),
	            target.getHeight(),
	            type,
	            path,
	            p
	    	) == false )
	    {
	        this.endExport();
	        return STATUS_FAILED;
	    }

	    this.endExport();
	    
	    return STATUS_SUCCEEDED;
	}

	/**
	 * 
	 *
	 */
	void moveFocusedObjectsToFront()
	{
		this.moveFocusedObjects( true );
	}


	/**
	 * 
	 *
	 */
	void moveFocusedObjectsToBack()
	{
		this.moveFocusedObjects( false );
	}


	/**
	 * 
	 *
	 */
	void moveFocusedObjects( final boolean toFront )
	{
		ArrayList fList = this.getVisibleFigureList();
		for( int ii=0; ii<fList.size(); ii++ )
		{
			SGFigure figure = (SGFigure)fList.get(ii);
			figure.moveFocusedObjects( toFront );
		}

		ArrayList list = this.getFocusedObjectsList();
		List objList = this.mFigureList;

		ArrayList objListOld = new ArrayList( objList );

		// move focused objects
		if( toFront )
		{
			for( int ii=0; ii<list.size(); ii++ )
			{
				Object obj = list.get(ii);
				SGUtility.moveObject( obj, objList, objList.size()-1 );
			}
		}
		else
		{
			for( int ii=list.size()-1; ii>=0; ii-- )
			{
				Object obj = list.get(ii);
				SGUtility.moveObject( obj, objList, 0 );
			}
		}

		if( objList.equals( objListOld ) == false )
		{
			this.setChanged(true);
			this.notifyToRoot();
			this.updateDataItem();
			this.repaintContentPane();
		}

	}



	/**
	 * 
	 * @param id
	 * @param toFront
	 * @return
	 */
	public boolean moveFigure( final int id, final boolean toFront )
	{
		SGFigure f = this.getFigure(id);
		if( f==null )
		{
			return false;
		}
		if( f.isVisible() == false )
		{
			return false;
		}

		List objList = this.mFigureList;
		List objListOld = new ArrayList( objList );

		// move focused objects
		if( toFront )
		{
			SGUtility.moveObject( f, objList, objList.size()-1 );
		}
		else
		{
			SGUtility.moveObject( f, objList, 0 );
		}

		final boolean ch = !this.mFigureList.equals( objListOld );
		if( ch )
		{
			this.setChanged(true);
			this.notifyToRoot();
			this.updateDataItem();
			this.repaintContentPane();
		}

		return true;
	}



	/**
	 * Copy the focused objects.
	 */
	public void doCopy()
	{
		this.copyFocusedObjects();
	}

	// Copy the focused objects.
	void copyFocusedObjects()
	{
		// get copied objects from all figures
		this.copyAllObjectsInVisibleFigures();

		// notify the copy command
		this.notifyToListener( MENUBARCMD_COPY );

		// update the menu items
		this.updateFocusedObjectItem();
	}


	/**
	 * Cut the focused objects.
	 */
	public void doCut()
	{
		this.cutFocusedObjects();
	}

	// Cut focused objects.
	private void cutFocusedObjects()
	{
		// get copied objects from all figures
		this.cutAllObjectsInVisibleFigures();

		// notify the cut command
		this.notifyToListener( MENUBARCMD_CUT );

		// notify the change to the root
		this.notifyToRoot();

		// update the menu items
		this.updateFocusedObjectItem();

		// repaint
		this.repaintContentPane();
	}


	/**
	 * 
	 * @param id
	 * @param isCopy
	 * @return
	 */
	public boolean cutOrCopyFigure( final int id, final boolean isCopy )
	{
		// get the figure
		SGFigure f = this.getFigure(id);
		if( f==null )
		{
			return false;
		}
		if( f.isVisible() == false )
		{
			return false;
		}

		// add to the attribute
		this.mCopiedFiguresList.add(f);

		// hide when cut the figure
		if( !isCopy )
		{
			this.hideFigure(f);
		}

		// notify the command
		if( isCopy )
		{
			this.notifyToListener( MENUBARCMD_COPY );
		}
		else
		{
			this.notifyToListener( MENUBARCMD_CUT );
		}

		// notify the change to the root
		this.notifyToRoot();

		// update the menu items
		this.updateFocusedObjectItem();

		// repaint
		this.repaintContentPane();

		return true;
	}


	/**
	 * Paste the copied objects.
	 */
	public void doPaste()
	{
		this.pasteCopiedObjects();
	}

	// Paste the copied objects.
	private void pasteCopiedObjects()
	{
		this.notifyToListener( MENUBARCMD_PASTE );

		// notify the change to the root
		this.notifyToRoot();
	}


	/**
	 * Duplicate the focused objects.
	 */
	public void doDuplicate()
	{
		this.duplicateFocusedObjects();
	}

	// Duplicate the focused objects.
	void duplicateFocusedObjects()
	{
		// duplicate child object of all figures
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			if( figure.duplicateFocusedObjects() == false )
			{
				return;
			}
		}

		// repaint after duplication
		this.repaintContentPane();


		// notify the duplication command
		this.notifyToListener( MENUBARCMD_DUPLICATE );

		// set unfocused the focused figures
		ArrayList fList = this.getFocusedObjectsList();
		for( int ii=0; ii<fList.size(); ii++ )
		{
			SGFigure figure = (SGFigure)fList.get(ii);
			this.setFocusedFigure( figure, false );
		}

		// set focused the duplicated figures
		ArrayList listNew = this.getVisibleFigureList();
		for( int ii=0; ii<listNew.size(); ii++ )
		{
			SGFigure figure = (SGFigure)listNew.get(ii);
			if( list.contains(figure) == false )
			{
				this.setFocusedFigure( figure, true );
			}
		}

		// notify the change to the root
		this.notifyToRoot();
	}


	/**
	 * Delete the focused objects.
	 */
	public void doDelete()
	{
		this.deleteFocusedObjects();
	}


	// Delete the focused objects.
	private void deleteFocusedObjects()
	{
		// hide all focused objects
		this.hideSelectedObjects();

		// notify the change to the root
		this.notifyToRoot();

		// update the menu items
		this.updateDataItem();

		// repaint
		this.repaintContentPane();
	}


	/**
	 * Hide the figure with given ID.
	 * @param id - ID of figure to hide
	 * @return true:succeeded, false:failed
	 */
	public boolean hideFigure( final int id )
	{
		SGFigure f = this.getFigure(id);
		if( f==null )
		{
			return false;
		}

		// hide the figure
		if( this.hideFigure(f) == false )
		{
			return false;
		}

		// clear the list
		this.clearFocusedObjects();

		// set changed flag
		this.setChanged(true);

		// notify the change to the root
		this.notifyToRoot();

		// update the menu items
		this.updateItemsByFigureNumbers();

		// repaint
		this.repaintContentPane();

		return true;
	}


	/**
	 * 
	 *
	 */
	private void cutAllObjectsInVisibleFigures()
	{
		this.cutOrCopyAllObjectsInVisibleFigures(false);
	}


	/**
	 * 
	 *
	 */
	private void copyAllObjectsInVisibleFigures()
	{
		this.cutOrCopyAllObjectsInVisibleFigures(true);
	}


	/**
	 * 
	 * @param isCopy
	 */
	private void cutOrCopyAllObjectsInVisibleFigures( final boolean isCopy )
	{
		// get objects from all figures
		ArrayList fList = this.getVisibleFigureList();
		ArrayList objList = new ArrayList();
		ArrayList dataList = new ArrayList();
		ArrayList dataNameList = new ArrayList();
		ArrayList propertiesMapList = new ArrayList();
		if( isCopy )
		{
			for( int ii=0; ii<fList.size(); ii++ )
			{
				SGFigure figure = (SGFigure)fList.get(ii);
				objList.addAll( figure.createCopiedObjects() );
				figure.createCopiedDataObjects( dataList, dataNameList, propertiesMapList );
			}
		}
		else
		{
			for( int ii=0; ii<fList.size(); ii++ )
			{
				SGFigure figure = (SGFigure)fList.get(ii);
				objList.addAll( figure.cutFocusedObjects() );
				figure.cutFocusedDataObjects( dataList, dataNameList, propertiesMapList );
			}
		}

		// copy data
		ArrayList dList = new ArrayList();
		SGUtility.copyObjects( dataList, dList );

		// set to the attribute
		this.clearCopiedObjectsList();
		this.mCopiedObjectsList.addAll( objList );
		this.mCopiedDataObjectsList.addAll( dList );
		this.mCopiedDataNameList.addAll( dataNameList );
		this.mCopiedDataPropertiesMapList.addAll( propertiesMapList );
		this.mCopiedFiguresList.addAll( this.getFocusedObjectsList() );
	}


	/**
	 * Returns the list of copied objects.
	 * @return list of copied objects
	 */
	public ArrayList getCopiedObjectsList()
	{
		return new ArrayList( this.mCopiedObjectsList );
	}


	/**
	 * Returns the list of copied data objects.
	 * @return list of copied data objects
	 */
	public ArrayList getCopiedObjectsDataList()
	{
		return new ArrayList( this.mCopiedDataObjectsList );
	}


	/**
	 * 
	 * @return
	 */
	public ArrayList getCopiedDataNameList()
	{
		return new ArrayList( this.mCopiedDataNameList );
	}


	/**
	 * 
	 * @return
	 */
	public ArrayList getCopiedDataPropertiesMapList()
	{
		return new ArrayList( this.mCopiedDataPropertiesMapList );
	}


	/**
	 * Clear the list of copied objects.
	 *
	 */
	private void clearCopiedObjectsList()
	{
		this.mCopiedObjectsList.clear();
		this.mCopiedDataObjectsList.clear();
		this.mCopiedDataNameList.clear();
		this.mCopiedDataPropertiesMapList.clear();
		this.mCopiedFiguresList.clear();
	}



	/**
	 * 
	 */
	void notifyPasteToFocusedFigures()
	{
		this.mPasteTargetList.clear();
		this.mPasteTargetList.addAll( this.getFocusedObjectsList() );
		this.notifyToListener( MENUBARCMD_PASTE );
	}


	/**
	 * The target object to paste the copied objects.
	 */
	private ArrayList mPasteTargetList = new ArrayList();


//	/**
//	 *
//	 */	
//	private void setLookAndFeel( String laf )
//	{
//		try
//		{
//			UIManager.setLookAndFeel(laf);
//			SwingUtilities.updateComponentTreeUI(this);
//		}
//		catch(Exception ex)
//		{
//			System.out.println("Error L&F Setting");
//		}
//	}


	/**
	 * 
	 * @return
	 */
	public boolean isLocked()
	{
		return this.mLockFigureFlag;
	}


	/**
	 * 
	 * @param b
	 * @return
	 */
	public boolean setLocked( final boolean b )
	{
		this.mLockFigureFlag = b;
		this.updateLockItems();
		return true;
	}

	
	/**
	 * 
	 * @return
	 */
	private void updateLockItems()
	{
		final boolean flag = this.isLocked();

		// set the toggle button
		this.mToolBar.setButtonSelected( MENUBARCMD_LOCK, flag );

		this.mMenuBar.setMenuItemSelected( MENUBAR_LAYOUT, MENUBARCMD_LOCK, flag );
	}



	/**
	 * Discard all objects in the undo buffer.
	 */
	public void clearUndoBuffer()
	{
		final boolean saved = this.isSaved();

		this.initUndoBuffer();

		// set the saved index
		if( saved )
		{
			this.mSavedListIndex = 0;
			this.updateStatusBarSavedFlag();
		}
		else
		{
			this.initSavedHistory();
		}

		// remove useless figures
		this.removeUselessFigures();

		// update items in the menu bar
		this.updateUndoItems();
	}


	/**
	 * 
	 * @param idArray
	 * @return
	 */
	public boolean setSelectedFigure( final int[] idArray )
	{
		for( int ii=0; ii<idArray.length; ii++ )
		{
			SGFigure f = this.getFigure( idArray[ii] );
			f.setSelected(true);
		}
		this.updateFocusedObjectItem();
		this.repaintContentPane();
		return true;
	}



	/**
	 * 
	 * @return
	 */
	public String getClassDescription()
	{
		return this.getInstanceDescription();
	}


	/**
	 * 
	 * @return
	 */
	public String getInstanceDescription()
	{
		return "Window: "+this.mID;
	}


	/**
	 * 
	 *
	 */
	private void createPropertyMenuBarItem()
	{
		// create an action event listener instance
		TreeMenuItemListener l = new TreeMenuItemListener();

		this.mMenuBar.createPropertyMenuBarItem( this, l );
	}


	/**
	 * An action event listener class for the menu items with the tree structure.
	 *
	 */
	private class TreeMenuItemListener implements ActionListener
	{
		public void actionPerformed( ActionEvent e )
		{
			Object source = e.getSource();
			NodeMenuItem item = (NodeMenuItem)source;

			SGINode node = item.node;
			SGIPropertyDialogObserver obs = (SGIPropertyDialogObserver)node;
			SGPropertyDialog dg = obs.getPropertyDialog();

			SGDrawingWindow.this.showPropertyDialog(dg,obs);
		}
	};



	/**
	 * 
	 * @param sb
	 * @return
	 */
	public boolean createTree( StringBuffer sb )
	{
		this.createTree( this, sb, 0 );
		return true;
	}

	private void createTree( final SGINode node, final StringBuffer sb, final int depth )
	{
		final ArrayList childList = node.getChildNodes();
		final String cText = node.getClassDescription();
		final String iText = node.getInstanceDescription();
		final boolean pFlag = ( node instanceof SGIPropertyDialogObserver );

		// has child objects
		if( childList.size()!=0 )
		{
			if( cText!=null & iText!=null )
			{
				this.append( sb, iText, depth );

				final int d = depth+1;

				// add child
				for( int ii=0; ii<childList.size(); ii++ )
				{
					SGINode child = (SGINode)childList.get(ii);
					this.createTree( child, sb, d );
				}
			}
		}
		else
		{
			if( iText!=null )
			{
				// property dialog observer
				if( pFlag )
				{
					this.append( sb, iText, depth );
				}
			}
		}
	}


	private void append( final StringBuffer sb, final String str, final int depth )
	{
		final String s = "  ";
		for( int ii=0; ii<depth; ii++ )
		{
			sb.append(s);
		}
		sb.append( str );
		sb.append( SGIConstants.LINE_SEPARATOR );
	}

	//
	// implementation of the SGIProgressControl
	//

	/**
	 * Start progress timer
	 * @return results
	 */
	public boolean startProgress()
	{
	    return this.mStatusBar.startProgress();
	}
	
	/**
	 * terminate progress timer
	 * @return results
	 */
	public boolean endProgress()
	{
	    return this.mStatusBar.endProgress();
	}

	/**
	 * Set the progress value.
	 */
	public void setProgressValue( final float ratio )
	{
		this.mStatusBar.setProgressValue( ratio );
	}
	
	/**
	 * Set the progress value.
	 */
	public void setIndeterminateProgress( final boolean mode )
	{
		this.mStatusBar.setIndeterminateProgress( mode );
	}
	
	/**
	 * Set the messsage of the Progress bar.
	 * @param msg
	 *            progress messasge
	 */
	public void setProgressMessage( final String msg )
	{
		this.mStatusBar.setProgressMessage( msg );
	}
	
//
// About component bounds
//
	
	/**
	 * 
	 * @return
	 */
	private Rectangle2D getPaperRectInClientRect()
	{
		Rectangle2D rect = new Rectangle2D.Float(
			this.mPaperOrigin.x, this.mPaperOrigin.y,
			this.mClientPanel.getPaperWidth(), this.mClientPanel.getPaperHeight()
		);
		return rect;
	}
	
	
	private final Rectangle2D mTempPaperRect = new Rectangle2D.Float();


	/**
	 * 
	 * @return
	 */
	boolean recordPaperRect()
	{
		this.mTempPaperRect.setRect( this.getPaperRectInClientRect() );
		return true;
	}

	
	
	/**
	 * 
	 * @return
	 */
	public boolean isPaperBoundsChanged()
	{
		Rectangle temp = this.mTempPaperRect.getBounds();
		Rectangle present = this.getPaperRectInClientRect().getBounds();
		return !temp.equals(present);
	}


	/**
	 * Align all figures.
	 * @return
	 */
	public boolean alignFigures()
	{
		// record the location
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.recordFigureRect();
		}
		this.recordPaperRect();

		// aligns figures
		if( this.alignFiguresByGraphAreaNew() == false )
		{
			return false;
		}

		//
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			if( figure.isFigureMoved() )
			{
				figure.setChanged(true);
			}
		}

		if( this.isPaperBoundsChanged() )
		{
			this.setChanged(true);
		}
		
		// notify to the root
		this.notifyToRoot();

		return true;
	}



	/**
	 * 
	 * @param size
	 * @return
	 */
	public boolean setPaperSizeDirectly( final MediaSize size )
	{
		// record the previous size
		Rectangle pRect = this.getPaperRect().getBounds();

		// set the size of the paper
		this.mClientPanel.setPaperSize(size, this.getPaperPortrait() );

		// compare the size
		Rectangle rect = this.getPaperRect().getBounds();
		if( pRect.equals(rect) )
		{
			return true;
		}

		//
		this.updateClientRect();

		//
		this.setChanged(true);
		this.notifyToRoot();

		//
		this.zoomWithBoundingBox();

		return true;
	}





	private static final float BOUNDING_BOX_MARGIN;

	static
	{
		final float ratio = SGIConstants.CM_POINT_RATIO;
		final float ten = (float)SGUtilityNumber.getPowersOfTen( MINIMAL_LENGTH_ORDER );
		BOUNDING_BOX_MARGIN = ten/ratio;
	}



	/**
	 * 
	 * @return
	 */
	public boolean setBoundingBox()
	{
//		Rectangle pRect = this.getPaperRect().getBounds();
		Rectangle2D cRect = this.getClientRect();

		ArrayList list = this.getVisibleFigureList();
		if( list.size()!=0 )
		{
			// update the temporary rectangles
			for( int ii=0; ii<list.size(); ii++ )
			{
				SGFigure figure = (SGFigure)list.get(ii);
				figure.recordFigureRect();
			}
			this.recordPaperRect();

			// align figures
			Rectangle2D bbRect = this.getBoundingBoxOfFigures(list);
			for( int ii=0; ii<list.size(); ii++ )
			{
				SGFigure figure = (SGFigure)list.get(ii);
				float x = BOUNDING_BOX_MARGIN + (float)( cRect.getX() + figure.getGraphRectX() - bbRect.getX() );
				float y = BOUNDING_BOX_MARGIN + (float)( cRect.getY() + figure.getGraphRectY() - bbRect.getY() );
				figure.setGraphRectLocationRoundingOut(x,y);
				if( figure.isFigureMoved() )
				{
					figure.setChanged(true);
				}
			}

			//
			this.setFigureBoundingBox(0);

			//
			if( this.isPaperBoundsChanged() )
			{
				this.setChanged(true);
			}

			// notify to the root
			this.notifyToRoot();

		}
		else
		{
			SGUtility.showMessageDialog(
				this,
				"There is no figure.",
				"Failed to get the Bounding box.",
				JOptionPane.WARNING_MESSAGE );
		}

		return true;
	}



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


	//
	private void updatePaperItems()
	{
		this.mMenuBar.setMenuItemSelected(
			MENUBAR_LAYOUT, MENUBARCMD_PAPER_PORTRAIT, this.getPaperPortrait() );
	}


	/**
	 * 
	 */
	public boolean commit()
	{

		this.updateClientRect();
		this.zoomWithBoundingBox();
		this.updateGridItems();


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

		this.mTemporaryProperties = null;

		this.getContentPane().repaint();

		// notify the change to the root object
		this.notifyToRoot();

		return true;
	}


	/**
	 * 
	 */
	public boolean updateHistory()
	{
//System.out.println(this.isChanged());

		// update the updated index
		// this method must be called before SGUndoManager::updateHistory is called
		this.updateSavedListIndex();

		// get the changed flag before SGUndoManager::updateHistory is called
		final boolean changed = this.isChanged();

		// update the history
		SGUndoManager man = this.mUndoManager;
		if( man.updateHistory( this.getVisibleFigureList() ) == false )
		{
			return false;
		}

//System.out.println(this.getVisibleFigureList());
//System.out.println(man.getChangedObjectListList());

		// if the properties of window itself was changed
		if( changed )
		{
			// remove useless figures
			this.removeUselessFigures();
		}

//System.out.println();

		// update items
		this.updateUndoItems();

		// update the status bar
		this.updateStatusBarSavedFlag();

		return true;
	}


	// update the index the properties has changed
	private void updateSavedListIndex()
	{
		boolean changed = false;
		if( this.isChanged() )
		{
			changed = true;
		}
		else
		{
			ArrayList list = this.getVisibleFigureList();
			for( int ii=0; ii<list.size(); ii++ )
			{
				SGFigure f = (SGFigure)list.get(ii);
				if( f.isChanged() )
				{
					changed = true;
					break;
				}
			}
		}

		//
		if( changed )
		{
			final int index = this.mUndoManager.getChangedObjectListIndex();
			if( index < this.mSavedListIndex )
			{
				this.initSavedHistory();
			}
		}
	}


	//
	private void updateStatusBarSavedFlag()
	{
		boolean b = false;
		if( this.getVisibleFigureList().size()!=0 )
		{
			final int index = this.mUndoManager.getChangedObjectListIndex();
			b = (index!=this.mSavedListIndex);
		}
		this.mStatusBar.setSaved( b );
	}


	/**
	 * 
	 *
	 */
	public void initSavedHistory()
	{
		this.mSavedListIndex = -1;
		this.updateStatusBarSavedFlag();
	}


	//
	private boolean removeUselessFigures()
	{
		Set set = this.getAvailableChildSet();
		if( set.size()!=0 )
		{
			boolean gc = false;
			List cList = new ArrayList( this.mFigureList );
			for( int ii=cList.size()-1; ii>=0; ii-- )
			{
				Object obj = cList.get(ii);
				if( set.contains(obj) == false )
				{
					this.mFigureList.remove(obj);
					SGFigure f = (SGFigure)obj;
					f.dispose();
					gc = true;
				}
				obj = null;
			}

			if( gc )
			{
				cList.clear();
				set.clear();
				System.gc();
			}
		}

		return true;
	}


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

		return set;		
	}


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

		// figures
		List fList = this.getVisibleFigureList();
		for( int ii=0; ii<fList.size(); ii++ )
		{
			SGFigure f = (SGFigure)fList.get(ii);
			f.initUndoBuffer();
		}
	}


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

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


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

		this.mTemporaryProperties = null;

		//
		this.updateClientRect();


		this.getContentPane().repaint();

		return true;
	}




	/**
	 * 
	 */
	public boolean preview()
	{
		this.updateClientRect();
		this.zoomWithBoundingBox();
		this.updateGridItems();
		this.getContentPane().repaint();

		return true;
	}

	/**
	 * 
	 * @return
	 */
	public void setEnabled( boolean b )
	{
	    super.setEnabled( b );
	}
	
	/**
	 * 
	 * @return
	 */
	public void setWaitCursor( final boolean b )
	{
		final Cursor cur = (b)?  new Cursor( Cursor.WAIT_CURSOR ): new Cursor( Cursor.DEFAULT_CURSOR );
		RootPaneContainer root = (RootPaneContainer)this.getRootPane().getTopLevelAncestor();
		root.getGlassPane().setCursor( cur );
		root.getGlassPane().setVisible( b );
		if( b )
			root.getGlassPane().addMouseListener( new MouseAdapter(){} );
	}
	
	/**
	 * 
	 * @return
	 */
	private boolean setDialogProperty()
	{
		final SGWindowDialog dg = this.mDialog;
		
		// set title of dialog
		String title = SGWindowDialog.TITLE + " : " + this.getID();
		dg.setTitle(title);
		
		dg.setDialogProperty();

		return true;

	}



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



	/**
	 * 
	 * @return
	 */
	public boolean updateClientRect()
	{
		this.updateClientRectOld();
		return true;
	}

//private Rectangle2D mTempRect = new Rectangle2D.Double();



	/**
	 * ウインドウ内部のバウンディングボックスが変化したとき
	 * @return
	 */
	private boolean updateClientRectOld()
	{

		//
		// if the client rectangle does not contain the bounding box,
		// fit the client rectangle to the bounding box.
		//
		// if the viewport rectangle contains the bounding box,
		// fit the the client rect to the viewport rectangle.
		//

		// horizontal

		this.fitRect( this.mClientRect, this.getBoundingBox(), true );

		if( SGUtility.isRectContains(
			this.getViewportBounds(), this.getBoundingBox(), true ) )
		{
			this.fitRect( this.mClientRect, this.getViewportBounds(), true );
		}

		if( SGUtility.isRectContains(
			this.getClientRect(), this.getViewportBounds(), true ) == false )
		{
			Rectangle2D cRect = this.getClientRect();
			Rectangle2D vpRect = this.getViewportBounds();

			final boolean b1 = SGUtility.isRectContains( cRect, vpRect.getX(), true );
			final boolean b2 = SGUtility.isRectContains( cRect, vpRect.getX()+vpRect.getWidth(), true );

			double diff = 0.0;
			if( !b1 && b2 )
			{
				diff = vpRect.getX() - cRect.getX();
			}
			else if( b1 && !b2 )
			{
				diff = (vpRect.getX()+vpRect.getWidth())-(cRect.getX()+cRect.getWidth());
			}
			else if( !b1 && !b2 )
			{
				if( cRect.getX() < vpRect.getX() )
				{
					diff = (vpRect.getX()+vpRect.getWidth())-(cRect.getX()+cRect.getWidth());
				}
				else
				{
					diff = vpRect.getX() - cRect.getX();
				}
			}

			this.setClientRect(
				(float)( cRect.getX() + diff ),
				(float)cRect.getY(),
				(float)cRect.getWidth(),
				(float)cRect.getHeight()
			);
		}
				

		// vertical

		this.fitRect( this.mClientRect, this.getBoundingBox(), false );

		if( SGUtility.isRectContains(
			this.getViewportBounds(), this.getBoundingBox(), false ) )
		{
			this.fitRect( this.mClientRect, this.getViewportBounds(), false );
		}

		if( SGUtility.isRectContains(
			this.getClientRect(), this.getViewportBounds(), false ) == false )
		{
			Rectangle2D cRect = this.getClientRect();
			Rectangle2D vpRect = this.getViewportBounds();

			final boolean b1 = SGUtility.isRectContains( cRect, vpRect.getY(), false );
			final boolean b2 = SGUtility.isRectContains( cRect, vpRect.getY()+vpRect.getHeight(), false );

			double diff = 0.0;
			if( !b1 && b2 )
			{
				diff = vpRect.getY() - cRect.getY();
			}
			else if( b1 && !b2 )
			{
				diff = (vpRect.getY()+vpRect.getHeight())-(cRect.getY()+cRect.getHeight());
			}
			else if( !b1 && !b2 )
			{
				if( cRect.getY() < vpRect.getY() )
				{
					diff = (vpRect.getY()+vpRect.getHeight())-(cRect.getY()+cRect.getHeight());
				}
				else
				{
					diff = vpRect.getY() - cRect.getY();
				}
			}

			this.setClientRect(
				(float)cRect.getX(),
				(float)( cRect.getY() + diff ),
				(float)cRect.getWidth(),
				(float)cRect.getHeight()
			);
		}

		final Rectangle2D bbRect = this.getBoundingBox();
		final Rectangle2D vpRect = this.getViewportBounds();
		final Rectangle2D cRect = this.getClientRect();
		
		this.mClientPanel.setScrollBarValue( cRect, vpRect );

		//
		this.mClientPanel.setEnableScrollBars( vpRect, bbRect );


		if( SGUtility.isRectContains( vpRect, bbRect, true ) )
		{
			this.fitRect( this.mClientRect, vpRect, true );
		}
		if( SGUtility.isRectContains( vpRect, bbRect, false ) )
		{
			this.fitRect( this.mClientRect, vpRect, false );
		}


		//
		this.mClientPanel.setScrollBarValue( cRect, vpRect );

		return true;
	}



	/**
	 * Fit rect1 to rect2.
	 * @param rect1
	 * @param rect2
	 * @param flag - true: x-direction, false: y-direction
	 */
	private void fitRect(
		Rectangle2D rect1, Rectangle2D rect2, final boolean flag )
	{
		if( flag )
		{
			rect1.setRect(
				rect2.getX(),
				rect1.getY(),
				rect2.getWidth(),
				rect1.getHeight()
			);
		}
		else
		{
			rect1.setRect(
				rect1.getX(),
				rect2.getY(),
				rect1.getWidth(),
				rect2.getHeight()
			);
		}
	}


	/**
	 * 
	 */
	public boolean setProperties( SGProperties p )
	{
//System.out.println("setProperties");

		if( ( p instanceof WindowProperties ) == false ) return false;

		WindowProperties wp = (WindowProperties)p;

		final Float w = wp.getPaperWidth();
		final Float h = wp.getPaperHeight();
		if( w==null || h==null )
		{
			return false;
		}

		this.mClientPanel.setPaperSize( w.floatValue(), h.floatValue() );

		final Color bgColor = wp.getBackgroundColor();
		if( bgColor==null )
		{
			return false;
		}
		this.mClientPanel.setPaperColor(bgColor);

		final Color gridColor = wp.getGridColor();
		if( gridColor==null )
		{
			return false;
		}
		this.mClientPanel.setGridLineColor(gridColor);

		final Boolean gridVisible = wp.getGridVisible();
		if( gridVisible==null )
		{
			return false;
		}
		this.mClientPanel.setGridLineVisible( gridVisible.booleanValue() );

		final Float gridInterval = wp.getGridInterval();
		if( gridInterval==null )
		{
			return false;
		}
		this.mClientPanel.setGridLineInterval(gridInterval.floatValue());

		final Float gridLineWidth = wp.getGridLineWidth();
		if( gridLineWidth==null )
		{
			return false;
		}
		this.mClientPanel.setGridLineWidth(gridLineWidth.floatValue());

		final Float imageLocationX = wp.getImageLocationX();
		if( imageLocationX==null )
		{
			return false;
		}
		this.mClientPanel.setImageLocationX( imageLocationX.floatValue() );

		final Float imageLocationY = wp.getImageLocationY();
		if( imageLocationY==null )
		{
			return false;
		}
		this.mClientPanel.setImageLocationY( imageLocationY.floatValue() );

		final Float imageScalingFactor = wp.getImageScalingFactor();
		if( imageScalingFactor==null )
		{
			return false;
		}
		this.mClientPanel.setImageScalingFactor( imageScalingFactor.floatValue() );

		final Image img = wp.getImage();
		this.mClientPanel.setImage(img);

		this.setVisibleFigure( wp.getVisibleFigureList() );

		return true;
	}


	/**
	 * 
	 * @return
	 */
	public SGProperties getProperties()
	{

		final WindowProperties p = new WindowProperties();

		p.setPaperWidth( this.mClientPanel.getPaperWidth() );
		p.setPaperHeight( this.mClientPanel.getPaperHeight() );
		p.setBackGroundColor( this.mClientPanel.getPaperColor() );
		p.setGridColor( this.mClientPanel.getGridLineColor() );
		p.setGridVisible( this.mClientPanel.isGridLineVisible() );
		p.setGridInterval( this.mClientPanel.getGridLineInterval() );
		p.setGridLineWidth( this.mClientPanel.getGridLineWidth() );
		p.setVisibleFigureList( this.getVisibleFigureList() );
		p.setImageLocationX( this.getImageLocationX() );
		p.setImageLocationY( this.getImageLocationY() );
		p.setImageScalingFactor( this.getImageScalingFactor() );
		p.setImage( this.getImage() );

		return p;

	}


	/**
	 * 
	 */
	protected boolean setVisibleFigure( final List list )
	{
		return SGUtility.setVisibleList( this.mFigureList, list );
	}



	/**
	 * 
	 * @return
	 */
	public Rectangle2D getClientRect()
	{
		if( this.mClientRect==null )
		{
			return null;
		}
		return (Rectangle2D)this.mClientRect.clone();
	}



//public void dumpClientRect()
//{
//	Rectangle2D rect = this.getClientRect();
//	final double x = rect.getX();
//	final double y = rect.getY();
//	final double w = rect.getWidth();
//	final double h = rect.getHeight();
//
//	System.out.println("x="+x*SGIConstants.CM_POINT_RATIO+"cm, y="+y*SGIConstants.CM_POINT_RATIO+"cm");
//	System.out.println("w="+w*SGIConstants.CM_POINT_RATIO+"cm, h="+h*SGIConstants.CM_POINT_RATIO+"cm");
//	System.out.println();
//}



//public void dumpRect()
//{
//	Rectangle2D cRect = this.getClientRect();
//	Rectangle2D vpRect = this.getViewportBounds();
//	Rectangle2D bbRect = this.getBoundingBox();
//
//	Rectangle2D cRect_ = new Rectangle2D.Float();
//	Rectangle2D vpRect_ = new Rectangle2D.Float();
//	Rectangle2D bbRect_ = new Rectangle2D.Float();
//
//	cRect_.setRect(
//		(float)cRect.getX()*SGIConstants.CM_POINT_RATIO,
//		(float)cRect.getY()*SGIConstants.CM_POINT_RATIO,
//		(float)cRect.getWidth()*SGIConstants.CM_POINT_RATIO,
//		(float)cRect.getHeight()*SGIConstants.CM_POINT_RATIO	
//	);
//
//	vpRect_.setRect(
//		(float)vpRect.getX()*SGIConstants.CM_POINT_RATIO,
//		(float)vpRect.getY()*SGIConstants.CM_POINT_RATIO,
//		(float)vpRect.getWidth()*SGIConstants.CM_POINT_RATIO,
//		(float)vpRect.getHeight()*SGIConstants.CM_POINT_RATIO	
//	);
//
//	bbRect_.setRect(
//		(float)bbRect.getX()*SGIConstants.CM_POINT_RATIO,
//		(float)bbRect.getY()*SGIConstants.CM_POINT_RATIO,
//		(float)bbRect.getWidth()*SGIConstants.CM_POINT_RATIO,
//		(float)bbRect.getHeight()*SGIConstants.CM_POINT_RATIO	
//	);
//
//	System.out.println("client:"+cRect_);
//	System.out.println("viewport:"+vpRect_);
//	System.out.println("bounding box:"+bbRect_);
//}


	/**
	 * 
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @return
	 */
	public boolean setClientRect(
		final float x, final float y, final float w, final float h )
	{
//this.dumpClientRect();
		this.mClientRect.setRect( x, y, w, h );
//System.out.println(this.mClientRect);
		return true;
	}


	/**
	 * 
	 * @param rect
	 * @return
	 */
	public boolean setClientRect( Rectangle2D rect )
	{
		this.mClientRect.setRect(rect);
		return true;
	}


	/**
	 * 
	 * @return
	 */
	public Rectangle2D getViewportBounds()
	{
		final SGTuple2f dim = this.getViewportSize();
		final float w = dim.x;
		final float h = dim.y;
		Rectangle2D rect = new Rectangle2D.Float( 0.0f, 0.0f, w, h );
		return rect;
	}



	/**
	 * ビューポートの境界の矩形を返す。
	 * mLayeredPane 内における位置で与えられる。
	 */
	public Rectangle2D getViewportBoundsInLayeredPane()
	{
		final SGTuple2f dim = this.getViewportSize();
		final float w = dim.x;
		final float h = dim.y;
		final int rw = this.mClientPanel.getRulerWidth();
		Rectangle2D rect = new Rectangle2D.Float( rw, rw, w, h );
		return rect;
	}



	/**
	 * 
	 * @return
	 */
	public Rectangle2D getViewportBoundsInComponent()
	{
		final int top = this.getTopWidth();
		final int left = this.getLeftWidth();
		final int rw = this.mClientPanel.getRulerWidth();
		final SGTuple2f dim = this.getViewportSize();
		final float w = dim.x + rw;
		final float h = dim.y + rw;
		Rectangle2D rect = new Rectangle2D.Float( left, top, w, h );
//System.out.println(rect);
		return rect;
	}



	/**
	 * 
	 * @return
	 */
	protected SGFigure[][] getOrderedFigureArray()
	{

		// get the visible figure list
		ArrayList list = this.getVisibleFigureList();


		// get the size of array
		final int n = list.size();
		if( n==0 )
		{
			return new SGFigure[0][0];
		}
		int size = 0;
		for( int ii=1; ii<=16; ii++ )
		{
			final int sqSmall = (ii-1)*(ii-1);
			final int sqLarge = ii*ii;
			if( (sqSmall<n) && (n<=sqLarge) )
			{
				size = ii;
				break;
			}
		}
		int sx = size;
		int div = n/sx;
		int sy = n%sx==0 ? div : div+1 ;


		// create a figure array
		final SGFigure[][] figureArray = new SGFigure[sy][sx];




		//
		// in the order of figure-ID
		//

		boolean flag = true;
		for( int ny=0; ny<sy; ny++ )
		{
			for( int nx=0; nx<sx; nx++ )
			{
				final int index = ny*sx + nx;
				if( index >= list.size() )
				{
					flag = false;
					break;
				}
				figureArray[ny][nx] = (SGFigure)list.get(index);
			}
			if( !flag )
			{
				break;
			}
		}


		return figureArray;
	}



	/**
	 * Returns a two dimensional array of figure list.
	 */
	private ArrayList[][] getFigureListArray()
	{
		// get the visible figure list
		ArrayList figureList = this.getVisibleFigureList();
		if( figureList.size()==0 )
		{
			return null;
		}


		// width of division
		float minWidth = Float.MAX_VALUE;
		float minHeight = Float.MAX_VALUE;
		for( int ii=0; ii<figureList.size(); ii++ )
		{
			SGFigure figure = (SGFigure)figureList.get(ii);
			Rectangle2D rect = figure.getGraphRect();
			if( rect.getWidth() < minWidth )
			{
				minWidth = (float)rect.getWidth();
			}
			if( rect.getHeight() < minHeight )
			{
				minHeight = (float)rect.getHeight();
			}
		}
		final float dx = minWidth;
		final float dy = minHeight;

		Rectangle2D bbRect = this.getBoundingBoxOfFigures(figureList);

		final int numX = (int)( (float)bbRect.getWidth()/dx ) + 1;
		final int numY = (int)( (float)bbRect.getHeight()/dy ) + 1;


		// get a two-dimensional array of figures
		ArrayList[][] fListArray = new ArrayList[numX][numY];
		for( int ii=0; ii<numX; ii++ )
		{
			for( int jj=0; jj<numY; jj++ )
			{
				fListArray[ii][jj] = new ArrayList();
			}
		}
		for( int ii=0; ii<figureList.size(); ii++ )
		{
			SGFigure figure = (SGFigure)figureList.get(ii);
			Rectangle2D gRect = figure.getGraphRect();
			int nx = (int)( ( gRect.getCenterX() - bbRect.getX() )/dx );
			int ny = (int)( ( gRect.getCenterY() - bbRect.getY() )/dy );
			fListArray[nx][ny].add( figure );
		}


		ArrayList numListX = new ArrayList();
		for( int nx=0; nx<numX; nx++ )
		{
			boolean flag = false;
			for( int ny=0; ny<numY; ny++ )
			{
				if( fListArray[nx][ny].size()!=0 )
				{
					flag = true;
					break;
				}
			}
			if( flag )
			{
				numListX.add( new Integer(nx) );
			}
		}

		ArrayList numListY = new ArrayList();
		for( int ny=0; ny<numY; ny++ )
		{
			boolean flag = false;
			for( int nx=0; nx<numX; nx++ )
			{
				if( fListArray[nx][ny].size()!=0 )
				{
					flag = true;
					break;
				}
			}
			if( flag )
			{
				numListY.add( new Integer(ny) );
			}
		}


		final int sx = numListX.size();
		final int sy = numListY.size();

		ArrayList[][] figureListArray = new ArrayList[sx][sy];
		for( int ii=0; ii<sx; ii++ )
		{
			final int nx = ((Integer)numListX.get(ii)).intValue();
			for( int jj=0; jj<sy; jj++ )
			{
				final int ny = ((Integer)numListY.get(jj)).intValue();
				figureListArray[ii][jj] = fListArray[nx][ny];
			}
		}

		return figureListArray;		
	}



	/**
	 * 
	 * @return
	 */
	private boolean alignFiguresLeftAndBottom( ArrayList[][] figureListArray )
	{
		final int sx = figureListArray.length;
		final int sy = figureListArray[0].length;

		//
		final float[][] topArray = new float[sx][sy];
		final float[][] bottomArray = new float[sx][sy];
		final float[][] leftArray = new float[sx][sy];
		final float[][] rightArray = new float[sx][sy];
		for( int ii=0; ii<sx; ii++ )
		{
			for( int jj=0; jj<sy; jj++ )
			{
				ArrayList list = figureListArray[ii][jj];
				float maxTop = 0.0f;
				float maxBottom = 0.0f;
				float maxLeft = 0.0f;
				float maxRight = 0.0f;
				for( int kk=0; kk<list.size(); kk++ )
				{
					SGFigure figure = (SGFigure)list.get(kk);
					Rectangle2D rect = figure.getGraphRect();
					final float width = (float)rect.getWidth();
					final float height = (float)rect.getHeight();
					SGTuple2f tb = new SGTuple2f();
					SGTuple2f lr = new SGTuple2f();
					figure.calcMargin( tb, lr );
					final float top = tb.x;
					final float bottom = tb.y;
					final float left = lr.x;
					final float right = lr.y;
					if( top + height > maxTop )
					{
						maxTop = top + height;
					}
					if( bottom > maxBottom )
					{
						maxBottom = bottom;
					}
					if( left > maxLeft )
					{
						maxLeft = left;
					}
					if( right + width > maxRight )
					{
						maxRight = right + width;
					}
				}

				topArray[ii][jj] = maxTop;
				bottomArray[ii][jj] = maxBottom;
				leftArray[ii][jj] = maxLeft;
				rightArray[ii][jj] = maxRight;
			}
		}


		// get arrays of the width and the height
		final float[] widthArray = new float[sx];
		for( int nx=0; nx<sx; nx++ )
		{
			float wMax = 0.0f;
			for( int ny=0; ny<sy; ny++ )
			{
				float width = leftArray[nx][ny] + rightArray[nx][ny];
				if( width > wMax )
				{
					wMax = width;
				}
			}
			widthArray[nx] = wMax;
		}

		final float[] heightArray = new float[sy];
		for( int ny=0; ny<sy; ny++ )
		{
			float hMax = 0.0f;
			for( int nx=0; nx<sx; nx++ )
			{
				float height = topArray[nx][ny] + bottomArray[nx][ny];
				if( height > hMax )
				{
					hMax = height;
				}
			}
			heightArray[ny] = hMax;
		}


		// get arrays of the width and the height
		final float[] maxLeftArray = new float[sx];
		for( int nx=0; nx<sx; nx++ )
		{
			float wMax = 0.0f;
			for( int ny=0; ny<sy; ny++ )
			{
				float width = leftArray[nx][ny];
				if( width > wMax )
				{
					wMax = width;
				}
			}
			maxLeftArray[nx] = wMax;
		}

		final float[] maxRightArray = new float[sx];
		for( int nx=0; nx<sx; nx++ )
		{
			float wMax = 0.0f;
			for( int ny=0; ny<sy; ny++ )
			{
				float width = rightArray[nx][ny];
				if( width > wMax )
				{
					wMax = width;
				}
			}
			maxRightArray[nx] = wMax;
		}


		final float[] maxBottomArray = new float[sy];
		for( int ny=0; ny<sy; ny++ )
		{
			float hMax = 0.0f;
			for( int nx=0; nx<sx; nx++ )
			{
				float height = bottomArray[nx][ny];
				if( height > hMax )
				{
					hMax = height;
				}
			}
			maxBottomArray[ny] = hMax;
		}


		// create arrays of the coordinate of the left-bottom corner
		final float diff = this.getMagnification() * this.mClientPanel.getGridLineInterval();
		Rectangle2D pRect = this.getPaperRect();
		final float px = (float)pRect.getX();
		final float py = (float)pRect.getY();
		float x = px;
		float y = py;
		final float[] originXArray = new float[sx];
		for( int nx=0; nx<sx; nx++ )
		{
			final float value = x + maxLeftArray[nx];
			final int index = (int)((value-px)/diff) +1;
			originXArray[nx] = px + index*diff;
			x = originXArray[nx] + maxRightArray[nx];
		}
		final float[] originYArray = new float[sy];
		for( int ny=0; ny<sy; ny++ )
		{
			final float value = y + heightArray[ny] - maxBottomArray[ny];
			final int index = (int)((value-py)/diff) +1;
			originYArray[ny] = py + index*diff;
			y = originYArray[ny] + maxBottomArray[ny];
		}


		// set the location of figures
		boolean flag = true;
		for( int ny=0; ny<sy; ny++ )
		{
			for( int nx=0; nx<sx; nx++ )
			{
				ArrayList list = figureListArray[nx][ny];
				for( int ii=0; ii<list.size(); ii++ )
				{
					SGFigure figure = (SGFigure)list.get(ii);
					if( figure==null )
					{
						flag = false;
						break;
					}

					if( figure.setGraphRectLocationByLeftBottom(
						originXArray[nx], originYArray[ny] ) == false )
					{
						return false;
					}
				}
				if( !flag )
				{
					break;
				}
			}
			if( !flag )
			{
				break;
			}
		}

		return true;
	}



//	private Float findCeilingValue( final float[] array, final float value )
//	{
//		float[] copy = (float[])array.clone();
//		Arrays.sort(copy);
//		
//		for( int ii=0; ii<copy.length; ii++ )
//		{
//			if( value <= copy[ii] )
//			{
//				return new Float( copy[ii] );
//			}
//		}
//
//		return null;
//	}




	/**
	 * 
	 * @return
	 */
	public boolean alignFiguresByGraphArea()
	{
		// get the visible figure list
		ArrayList figureList = this.getVisibleFigureList();
		if( figureList.size()==0 )
		{
			return true;
		}

		Rectangle2D cRect = this.mClientRect;


		// width of division
		float minWidth = Float.MAX_VALUE;
		float minHeight = Float.MAX_VALUE;
		for( int ii=0; ii<figureList.size(); ii++ )
		{
			SGFigure figure = (SGFigure)figureList.get(ii);
			Rectangle2D rect = figure.getGraphRect();
			if( rect.getWidth() < minWidth )
			{
				minWidth = (float)rect.getWidth();
			}
			if( rect.getHeight() < minHeight )
			{
				minHeight = (float)rect.getHeight();
			}
		}
		final float dx = minWidth;
		final float dy = minHeight;

		Rectangle2D bbRect = this.getBoundingBoxOfFigures(figureList);

//		final int numX = (int)( (float)cRect.getWidth()/dx ) + 1;
//		final int numY = (int)( (float)cRect.getHeight()/dy ) + 1;

final int numX = (int)( (float)bbRect.getWidth()/dx ) + 1;
final int numY = (int)( (float)bbRect.getHeight()/dy ) + 1;


		// get a two-dimensional array of figures
		ArrayList[][] fListArray = new ArrayList[numX][numY];
		for( int ii=0; ii<numX; ii++ )
		{
			for( int jj=0; jj<numY; jj++ )
			{
				fListArray[ii][jj] = new ArrayList();
			}
		}
		for( int ii=0; ii<figureList.size(); ii++ )
		{
			SGFigure figure = (SGFigure)figureList.get(ii);
			Rectangle2D gRect = figure.getGraphRect();

//			int nx = (int)( ( gRect.getCenterX() - cRect.getX() )/dx );
//			int ny = (int)( ( gRect.getCenterY() - cRect.getY() )/dy );

int nx = (int)( ( gRect.getCenterX() - bbRect.getX() )/dx );
int ny = (int)( ( gRect.getCenterY() - bbRect.getY() )/dy );

			fListArray[nx][ny].add( figure );
		}


		ArrayList numListX = new ArrayList();
		for( int nx=0; nx<numX; nx++ )
		{
			boolean flag = false;
			for( int ny=0; ny<numY; ny++ )
			{
				if( fListArray[nx][ny].size()!=0 )
				{
					flag = true;
					break;
				}
			}
			if( flag )
			{
				numListX.add( new Integer(nx) );
			}
		}

		ArrayList numListY = new ArrayList();
		for( int ny=0; ny<numY; ny++ )
		{
			boolean flag = false;
			for( int nx=0; nx<numX; nx++ )
			{
				if( fListArray[nx][ny].size()!=0 )
				{
					flag = true;
					break;
				}
			}
			if( flag )
			{
				numListY.add( new Integer(ny) );
			}
		}


		final int sx = numListX.size();
		final int sy = numListY.size();

		ArrayList[][] figureListArray = new ArrayList[sx][sy];
		for( int ii=0; ii<sx; ii++ )
		{
			final int nx = ((Integer)numListX.get(ii)).intValue();
			for( int jj=0; jj<sy; jj++ )
			{
				final int ny = ((Integer)numListY.get(jj)).intValue();
				figureListArray[ii][jj] = fListArray[nx][ny];
			}
		}



		//
		float[][] topArray = new float[sx][sy];
		float[][] bottomArray = new float[sx][sy];
		float[][] leftArray = new float[sx][sy];
		float[][] rightArray = new float[sx][sy];
		for( int ii=0; ii<sx; ii++ )
		{
			for( int jj=0; jj<sy; jj++ )
			{
				if( figureListArray[ii][jj] == null )
				{
					continue;
				}

				ArrayList list = figureListArray[ii][jj];
				float maxTop = 0.0f;
				float maxBottom = 0.0f;
				float maxLeft = 0.0f;
				float maxRight = 0.0f;
				for( int kk=0; kk<list.size(); kk++ )
				{
					SGFigure figure = (SGFigure)list.get(kk);
					Rectangle2D rect = figure.getGraphRect();
					SGTuple2f tb = new SGTuple2f();
					SGTuple2f lr = new SGTuple2f();
					figure.calcMargin( tb, lr );
					if( tb.x + (float)rect.getHeight() > maxTop )
					{
						maxTop = tb.x + (float)rect.getHeight();
					}
					if( tb.y > maxBottom )
					{
						maxBottom = tb.y;
					}
					if( lr.x > maxLeft )
					{
						maxLeft = lr.x;
					}
					if( lr.y + (float)rect.getWidth() > maxRight )
					{
						maxRight = lr.y + (float)rect.getWidth();
					}
				}

				topArray[ii][jj] = maxTop;
				bottomArray[ii][jj] = maxBottom;
				leftArray[ii][jj] = maxLeft;
				rightArray[ii][jj] = maxRight;

			}
		}



		// get arrays of the width and the height
		final float[] widthArray = new float[sx];
		for( int nx=0; nx<sx; nx++ )
		{
			float wMax = 0.0f;
			for( int ny=0; ny<sy; ny++ )
			{
				float width = leftArray[nx][ny] + rightArray[nx][ny];
				if( width > wMax )
				{
					wMax = width;
				}
			}
			widthArray[nx] = wMax;
		}

		final float[] heightArray = new float[sy];
		for( int ny=0; ny<sy; ny++ )
		{
			float hMax = 0.0f;
			for( int nx=0; nx<sx; nx++ )
			{
				float height = topArray[nx][ny] + bottomArray[nx][ny];
				if( height > hMax )
				{
					hMax = height;
				}
			}
			heightArray[ny] = hMax;
		}


		// get arrays of the width and the height
		final float[] maxLeftArray = new float[sx];
		for( int nx=0; nx<sx; nx++ )
		{
			float wMax = 0.0f;
			for( int ny=0; ny<sy; ny++ )
			{
				float width = leftArray[nx][ny];
				if( width > wMax )
				{
					wMax = width;
				}
			}
			maxLeftArray[nx] = wMax;
		}

		final float[] maxBottomArray = new float[sy];
		for( int ny=0; ny<sy; ny++ )
		{
			float hMax = 0.0f;
			for( int nx=0; nx<sx; nx++ )
			{
				float height = bottomArray[nx][ny];
				if( height > hMax )
				{
					hMax = height;
				}
			}
			maxBottomArray[ny] = hMax;
		}


		// create arrays of the coordinate of the centers
		final float[] originXArray = new float[sx];
		float cx = (float)cRect.getX();
		for( int nx=0; nx<sx; nx++ )
		{
			originXArray[nx] = cx + maxLeftArray[nx];
			cx += widthArray[nx];
		}

		final float[] originYArray = new float[sy];
		float cy = (float)cRect.getY();
		for( int ny=0; ny<sy; ny++ )
		{
			originYArray[ny] = cy + heightArray[ny] - maxBottomArray[ny];
			cy += heightArray[ny];
		}



		// set the location of figures
		boolean flag = true;
		for( int ny=0; ny<sy; ny++ )
		{
			for( int nx=0; nx<sx; nx++ )
			{
				ArrayList list = figureListArray[nx][ny];
				for( int ii=0; ii<list.size(); ii++ )
				{
					SGFigure figure = (SGFigure)list.get(ii);
					if( figure==null )
					{
						flag = false;
						break;
					}

					if( figure.setGraphRectLocationByLeftBottom(
						originXArray[nx], originYArray[ny] ) == false )
					{
						return false;
					}
				}
				if( !flag )
				{
					break;
				}
			}
			if( !flag )
			{
				break;
			}
		}

/*
		// enlarge the size of paper
		int mode = -1;
		float wTotal = 0.0f;
		float hTotal = 0.0f;
		for( int ii=0; ii<widthArray.length; ii++ )
		{
			wTotal += widthArray[ii];
		}
		for( int ii=0; ii<heightArray.length; ii++ )
		{
			hTotal += heightArray[ii];
		}
		Rectangle2D pRect = this.getPaperRect();
		final boolean bw = ( pRect.getWidth() < wTotal );
		final boolean bh = ( pRect.getHeight() < hTotal );


		if( bw & bh )
		{
			mode = 0;
		}
		else if( bw )
		{
			mode = 1;
		}
		else if( bh )
		{
			mode = 2;
		}
*/

final int mode = 0;

//		if( mode!=-1 )
		{
			if( this.setFigureBoundingBox(mode) == false )
			{
				return false;
			}
		}


		return true;

	}



	/**
	 * 
	 * @return
	 */
	private boolean alignFiguresByGraphAreaNew()
	{

		// get the visible figure list
		ArrayList figureList = this.getVisibleFigureList();
		if( figureList.size()==0 )
		{
			return true;
		}


		// get a two-dimensional array of the list of figures
		ArrayList[][] figureListArray = this.getFigureListArray();
		if( figureListArray==null )
		{
			return false;
		}
		if( figureListArray.length==0 )
		{
			return false;
		}


		// align figures
		if( this.alignFiguresLeftAndBottom( figureListArray ) == false )
		{
			return false;
		}


		// set bounding box
		if( this.setFigureBoundingBox(0) == false )
		{
			return false;
		}

		return true;
	}




/*
	public static final double OVERLAP_RATIO = 0.50;


	private boolean isOverlapping( SGFigure figure1, SGFigure figure2, final boolean flag )
	{

		Rectangle2D rect1 = figure1.getGraphAreaRect();
		Rectangle2D rect2 = figure2.getGraphAreaRect();

		final double value = SGUtility.getOverlapping( rect1, rect2, flag );

		boolean ret = false;
		if( flag )
		{
			final double ratio1 = value/rect1.getWidth();
			final double ratio2 = value/rect2.getWidth();
			if( ratio1>OVERLAP_RATIO || ratio2>OVERLAP_RATIO )
			{
				ret = true;
			}
		}
		else
		{
			final double ratio1 = value/rect1.getHeight();
			final double ratio2 = value/rect2.getHeight();
			if( ratio1>OVERLAP_RATIO || ratio2>OVERLAP_RATIO )
			{
				ret = true;
			}
		}

		return ret;
	}

*/



	/**
	 * Returns the relative location of figure2 to figure1.
	 * @param figure1
	 * @param figure2
	 * @return		0:top 1:bottom 2:left 3:right
	 */
/*	private int getAlignment( SGFigure figure1, SGFigure figure2 )
	{
		Rectangle2D rect1 = figure1.getGraphAreaRect();
		Rectangle2D rect2 = figure2.getGraphAreaRect();

		final double vx = rect2.getCenterX() - rect1.getCenterX();
		final double vy = rect2.getCenterY() - rect1.getCenterY();

		final double angle = Math.atan2(vy,vx);


		int num = -1;
		if( -0.75*Math.PI<=angle && angle<-0.25*Math.PI )
		{
			num = 0;
		}
		else if( 0.25*Math.PI<=angle && angle<0.75*Math.PI )
		{
			num = 1;
		}
		else if( ( -Math.PI<=angle && angle<-0.75*Math.PI )
			|| ( 0.75*Math.PI<=angle && angle<=Math.PI ) )
		{
			num = 2;
		}
		else if( -0.25*Math.PI<=angle && angle<0.25*Math.PI )
		{
			num = 3;
		}

		return num;
	}
*/


/*
	class Figure
	{
		SGFigure fig;
		Figure top;
		Figure bottom;
		Figure left;
		Figure right;
		Figure topLeft;
		Figure topRight;
		Figure bottomLeft;
		Figure bottomRight;
		ArrayList topList = new ArrayList();
		ArrayList bottomList = new ArrayList();
		ArrayList leftList = new ArrayList();
		ArrayList rightList = new ArrayList();

		public String toString()
		{
			if( fig==null )
			{
				return "null";
			}
			else
			{
				return fig.toString();
			}
		}
	}
*/





	/**
	 * Order the figures.
	 * @return
	 */
	public boolean alignFiguresByBoundingBox()
	{
		boolean flag;


		final SGFigure[][] figureArray = this.getOrderedFigureArray();
		if( figureArray==null )
		{
			return false;
		}
		if( figureArray.length==0 )
		{
			return true;
		}
		final int sy = figureArray.length;
		final int sx = figureArray[0].length;


		// create an array of the bounding box of the figures
		Rectangle2D[][] rectArray = new Rectangle2D[sy][sx];
		flag = true;
		for( int ny=0; ny<sy; ny++ )
		{
			for( int nx=0; nx<sx; nx++ )
			{
				if( figureArray[ny][nx] == null )
				{
					flag = false;
					break;
				}
				rectArray[ny][nx] = figureArray[ny][nx].getBoundingBox();
			}
			if( !flag )
			{
				break;
			}
		}

/*
for( int ny=0; ny<sy; ny++ )
{
	for( int nx=0; nx<sx; nx++ )
	{
		System.out.println(ny+"  "+nx+"  "+array[ny][nx]);
	}
}
System.out.println();
*/

		// get arrays of the width and the height
		final float[] widthArray = new float[sx];
		for( int nx=0; nx<sx; nx++ )
		{
			float wMax = 0.0f;
			for( int ny=0; ny<sy; ny++ )
			{
				Rectangle2D rect = rectArray[ny][nx];
				if( rect==null )
				{
					break;
				}
				float width = (float)rectArray[ny][nx].getWidth();
				if( width > wMax )
				{
					wMax = width;
				}
			}
			widthArray[nx] = wMax;
		}

		final float[] heightArray = new float[sy];
		for( int ny=0; ny<sy; ny++ )
		{
			float hMax = 0.0f;
			for( int nx=0; nx<sx; nx++ )
			{
				Rectangle2D rect = rectArray[ny][nx];
				if( rect==null )
				{
					break;
				}
				float height = (float)rectArray[ny][nx].getHeight();
				if( height > hMax )
				{
					hMax = height;
				}
			}
			heightArray[ny] = hMax;
		}

/*
for( int ii=0; ii<sx; ii++ )
{
	System.out.println(ii+"  "+widthArray[ii]);
}
System.out.println();

for( int ii=0; ii<sy; ii++ )
{
	System.out.println(ii+"  "+heightArray[ii]);
}
System.out.println();
*/

		// create arrays of the coordinate of the centers

		Rectangle2D cRect = this.getClientRect();

		final float[] centerXArray = new float[sx];
		float cx = (float)cRect.getX();
		for( int nx=0; nx<sx; nx++ )
		{
			centerXArray[nx] = cx + widthArray[nx]/2.0f;
			cx += widthArray[nx];
		}

		final float[] centerYArray = new float[sy];
		float cy = (float)cRect.getY();
		for( int ny=0; ny<sy; ny++ )
		{
			centerYArray[ny] = cy + heightArray[ny]/2.0f;
			cy += heightArray[ny];
		}

/*
for( int ii=0; ii<sx; ii++ )
{
	System.out.println(ii+"  "+centerXArray[ii]);
}
System.out.println();

for( int ii=0; ii<sy; ii++ )
{
	System.out.println(ii+"  "+centerYArray[ii]);
}
System.out.println();
*/

		// set the location of figures

		flag = true;
		for( int ny=0; ny<sy; ny++ )
		{
			for( int nx=0; nx<sx; nx++ )
			{
//				final int index = ny*sx + nx;
				SGFigure figure = figureArray[ny][nx];
				if( figure==null )
				{
					flag = false;
					break;
				}
				if( figure.setCenter(
					centerXArray[nx], centerYArray[ny] ) == false )
				{
					return false;
				}
			}
			if( !flag )
			{
				break;
			}
		}



		// enlarge the size of paper
		int mode = -1;
		float wTotal = 0.0f;
		float hTotal = 0.0f;
		for( int ii=0; ii<widthArray.length; ii++ )
		{
			wTotal += widthArray[ii];
		}
		for( int ii=0; ii<heightArray.length; ii++ )
		{
			hTotal += heightArray[ii];
		}
		Rectangle2D pRect = this.getPaperRect();
		final boolean bw = ( pRect.getWidth() < wTotal );
		final boolean bh = ( pRect.getHeight() < hTotal );

//System.out.println("pRect:"+pRect);
//System.out.println("wTotal="+wTotal);
//System.out.println("hTotal="+hTotal);

		if( bw & bh )
		{
			mode = 0;
		}
		else if( bw )
		{
			mode = 1;
		}
		else if( bh )
		{
			mode = 2;
		}

//System.out.println("mode="+mode);
//System.out.println();

		if( mode!=-1 )
		{
			if( this.setFigureBoundingBox(mode) == false )
			{
				return false;
			}
		}


		return true;
	}





//
//
// アンドゥ関係
//
//


	private SGUndoManager mUndoManager = new SGUndoManager( this );



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

	public boolean setMemento( SGProperties p )
	{
		return this.setProperties(p);
	}



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

//		this.repaintAll();

		return true;
	}


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

//		this.repaintAll();

		return true;
	}



	boolean updateGraphRectOfAllFigures()
	{
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.updateGraphRect();
		}
		return true;
	}



	/**
	 * Update method called on undo and redo.
	 */
	private boolean updateOnUndo()
	{
		// clear all focused objects
		this.clearAllFocusedObjectsInFigures();

		// update the location and scroll values
		this.updateClientRect();
//		this.setScrollBarValue();

		// update the graph rectangle of figures
		this.updateGraphRectOfAllFigures();

		// update menu items
		this.updateUndoItems();
		this.updateItemsByFigureNumbers();
		this.updateDataItem();
		this.updateGridItems();

		return true;
	}


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

		// update
		this.updateOnUndo();
		this.updateStatusBarSavedFlag();

		return true;
	}


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

		// update
		this.updateOnUndo();
		this.updateStatusBarSavedFlag();

		return true;
	}



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


	/**
	 * 
	 * @return
	 */
	public boolean isChangedRoot()
	{
		if( this.isChanged() )
		{
			return true;
		}
		List fList = this.getVisibleFigureList();
		for( int ii=0; ii<fList.size(); ii++ )
		{
			SGIUndoable obj = (SGIUndoable)fList.get(ii);
			if( obj.isChangedRoot() )
			{
				return true;
			}
		}
		return false;
	}


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

	
	/**
	 * 
	 */
	private void updateUndoItems()
	{
		final boolean undoEnable = this.mUndoManager.isUndoable();
		final boolean redoEnable = this.mUndoManager.isRedoable();

		// menu bar
		SGMenuBar mBar = this.mMenuBar;
		mBar.setMenuItemEnabled( MENUBAR_EDIT, MENUBARCMD_UNDO, undoEnable );
		mBar.setMenuItemEnabled( MENUBAR_EDIT, MENUBARCMD_REDO, redoEnable );


		// tool bar
		SGToolBar tBar = this.mToolBar;
		tBar.setButtonEnabled( MENUBARCMD_UNDO, undoEnable );
		tBar.setButtonEnabled( MENUBARCMD_REDO, redoEnable );
	}



	/**
	 * 
	 *
	 */
	public void notifyToRoot()
	{
//System.out.println("notifyToRoot");
		this.updateHistoryTree();
	}



	/**
	 * 
	 * @return
	 */
	private boolean updateHistoryTree()
	{
		return this.updateHistory();
	}




//
// メニュー関係
//

	private SGMenuBar mMenuBar = new SGMenuBar();


	/**
	 * 
	 */
	private boolean createMenuBar()
	{
		SGMenuBar menuBar = this.mMenuBar;
		this.setJMenuBar( menuBar );
		menuBar.addActionListener( this );
		menuBar.addMenuListener( this );

		return true;
	}



	/**
	 * Used for the menu items with tree structure.
	 */
	static class NodeMenuItem extends JMenuItem
	{
		/**
		 * 
		 */
		private static final long serialVersionUID = 8728676638490853269L;
		SGINode node;
		NodeMenuItem( String text )
		{
			super( text );
		}

		/**
		 * @return  node を戻します。
		 * @uml.property  name="node"
		 */
		SGINode getNode()
		{
			return this.node;
		}
		
		/**
		 * @param node  設定する node。
		 * @uml.property  name="node"
		 */
		void setNode( SGINode _node )
		{
			this.node = _node;
		}
	}



	/**
	 * 
	 * @return
	 */
	public boolean exportAsImage( final boolean silent )
	{
		return this.toImage( EXPORT, silent );
	}



	private InfoForExport mExportInfo = null;
	private ExportPanel mExportTarget = null;

	public boolean startExport()
	{
		final int width = (int)this.mClientPanel.getPaperWidth();
		final int height = (int)this.mClientPanel.getPaperHeight();

		InfoForExport info = new InfoForExport();
		ExportPanel target = new ExportPanel();
		target.setOpaque(true);
		target.setBackground( this.mClientPanel.getPaperColor() );
		target.setPreferredSize( new Dimension(width,height) );

		this.beforeExport( target, info );

		this.mExportTarget = target;
		this.mExportInfo = info;

		return true;
	}

	public boolean endExport()
	{
		this.afterExport( this.mExportTarget, this.mExportInfo );
		this.mExportTarget = null;
		this.mExportInfo = null;
		return true;
	}

	public Component getExportTarget()
	{
		return this.mExportTarget;
	}


//	/**
//	 * 
//	 * @return
//	 */
//	public boolean exportAsImage( final Graphics g, final String path, final boolean silent )
//	{
//		final int width = (int)this.mPaperSize.x;
//		final int height = (int)this.mPaperSize.y;
//
//		InfoForExport info = new InfoForExport();
//		ExportPanel target = new ExportPanel();
//		target.setOpaque(true);
//		target.setBackground( this.getPaperColor() );
//		target.setPreferredSize( new Dimension(width,height) );
//
//		this.beforeExport( target, info );
//
//
//		File f = new File(path);
//
//		Dimension dim = new Dimension(width,height);
//		Properties prop = new Properties();
//
//		g.startExport(); 
//		target.print(g); 
//		g.endExport();
//
//
//		this.afterExport( target, info );
//
//		return true;
//	}


	/**
	 * 
	 * @return
	 */
	public boolean printImage( final boolean silent )
	{
		return this.toImage( PRINT, silent );
	}


	private boolean toImage( final int mode, final boolean silent )
	{
		final int width = (int)this.mClientPanel.getPaperWidth();
		final int height = (int)this.mClientPanel.getPaperHeight();

		InfoForExport info = new InfoForExport();
		ExportPanel target = new ExportPanel();
		target.setOpaque(true);
		target.setBackground( this.mClientPanel.getPaperColor() );
		target.setPreferredSize( new Dimension(width,height) );

		this.beforeExport( target, info );

		SGIImageExportManager man = this.mImageExportManager;

		boolean ret;
		switch( mode )
		{
			case EXPORT :
			{
				// export as image
				ret = man.export( target, this, width, height, silent );
				break;
			}
			
			case PRINT :
			{
				// print as image
				ret = man.print( target, this, width, height, silent );
				break;
			}
			
			default :
			{
				ret = false;
			}
		}

		this.afterExport( target, info );

		return ret;
	}



	/**
	 * A class used for the image export.
	 */
	private static class InfoForExport
	{
		float mag;
		float hValue;
		float vValue;
		/**
		 * @uml.property  name="locationArray"
		 * @uml.associationEnd  multiplicity="(0 -1)"
		 */
		SGTuple2f[] locationArray;
		ArrayList visibleList;
	}




	/**
	 * 
	 * @param ePanel
	 * @param info
	 * @return
	 */
	private boolean beforeExport(
		final ExportPanel ePanel, final InfoForExport info )
	{
		float mag = this.mMagnification;
		SGTuple2f value = this.mClientPanel.getScrollRatio();
		float hValue = value.x;
		float vValue = value.y;
		ArrayList list = this.getVisibleFigureList();

		// check whether the figures run off the edge of the paper
		boolean isInside = true;
		Rectangle pRect = this.getPaperRect().getBounds();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			Rectangle rect = figure.getBoundingBox().getBounds();
			if( pRect.contains(rect) == false )
			{
				isInside = false;
				break;
			}
		}
		if( !isInside )
		{
			SGUtility.showMessageDialog(
				this, "Some figures run off the edge of paper.",
				"Warning", JOptionPane.WARNING_MESSAGE );
		}


		// hide anchors temporarily
		this.setSelectionSymbolsVisible( false );
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.setSymbolsVisibleAroundAllObjects( false );
		}


		// record the location of figures
		SGTuple2f[] locationArray = new SGTuple2f[list.size()];
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			locationArray[ii] = new SGTuple2f(
				figure.mGraphRectX, figure.mGraphRectY
			);
		}

		// zoom
		this.zoom(1.0f);


		// set the location of figures
		Rectangle2D cRect = this.getClientRect();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.setGraphRectLocation(
				figure.getGraphRectX() - (float)cRect.getX(),
				figure.getGraphRectY() - (float)cRect.getY()
			);
		}


		//
		// set to the export panel
		//

		// set the location and the size of preview dialog
		final float width = this.mClientPanel.getPaperWidth();
		final float height = this.mClientPanel.getPaperHeight();

		// set the layered pane
		ePanel.setOpaque(false);
		ePanel.setLocation(0,0);
		ePanel.setSize( (int)width, (int)height );

		// add figures to the export panel
		Rectangle2D vBounds = new Rectangle2D.Float(
			0.0f, 0.0f, width, height );
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			ePanel.add( figure );
//			Rectangle bounds = new Rectangle(
//				0, 0, figure.getWidth(), figure.getHeight() );
			figure.setViewBounds( vBounds );
		}


		// set an image
		Image image = this.mClientPanel.getImage();
		if( image!=null )
		{
			SGTuple2f location = this.mClientPanel.getImageLocation();
			SGTuple2f size = this.mClientPanel.getImageSize();
			final float f = this.mClientPanel.getImageScalingFactor();
			ePanel.setImage( image, location.x, location.y, size.x, size.y, f );
		}


		// set invisible
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.setVisible(false);
		}


		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.setMode( MODE_EXPORT_AS_IMAGE );
		}


		// set information
		info.mag = mag;
		info.hValue = hValue;
		info.vValue = vValue;
		info.locationArray = locationArray;
		info.visibleList = list;

		return true;
	}


	/**
	 * 
	 * @param ePanel
	 * @param info
	 * @return
	 */
	private boolean afterExport(
		final ExportPanel ePanel, final InfoForExport info )
	{
		float mag = info.mag;
		float hValue = info.hValue;
		float vValue = info.vValue;
		SGTuple2f[] locationArray = info.locationArray;
		ArrayList list = info.visibleList;
			
		// zoom
		this.zoom(mag);

		// set scroll value
		this.mClientPanel.setScrollRatio(vValue, hValue);

		// set the location
//		SGTuple2f vpSize = this.getViewportSize();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.mGraphRectX = locationArray[ii].x;
			figure.mGraphRectY = locationArray[ii].y;
			figure.updateGraphRect();
			figure.setViewBounds();
		}

		// set visible
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.setVisible(true);
		}


		// rewrite hidden anchors
		this.setSelectionSymbolsVisible( true );
		ArrayList fList = this.getFocusedObjectsList();
		for( int ii=0; ii<fList.size(); ii++ )
		{
			SGFigure figure = (SGFigure)fList.get(ii);
			figure.setSymbolsVisibleAroundAllObjects( true );
		}

		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.setMode( MODE_DISPLAY );
		}

		this.repaintContentPane();

		return true;
	}



	/**
	 * A panel class used to export images.
	 */
	public static class ExportPanel extends JPanel
	{
		/**
		 * 
		 */
		private static final long serialVersionUID = 6760038313006131448L;

		/**
		 * The list of printable objects.
		 */
		private ArrayList mPrintableList = new ArrayList();
		
		/**
		 * 
		 */
		private boolean mClipFlag = true;

		private SGImage mImage = null;


		/**
		 * 
		 *
		 */		
		private ExportPanel()
		{
			super();
		}

		/**
		 * Add a figure.
		 * @param f - figure
		 */
		private void add( SGFigure f )
		{
			ArrayList list = this.mPrintableList;
			list.add(f);
			SGIFigureElement[] array
				= f.getIFigureElementArray();
			for( int ii=0; ii<array.length; ii++ )
			{
				list.add( array[ii] );
			}
		}

		/**
		 * Paint this object.
		 * @param - graphic context
		 */
		public void paintComponent( Graphics g )
		{
			super.paintComponent(g);

			if( this.mImage!=null )
			{
				this.mImage.drawImage(g);
			}

			final ArrayList list = this.mPrintableList;
			final boolean clip = this.getClipFlag();
			for( int ii=0; ii<list.size(); ii++ )
			{
				final SGIPaintable p = (SGIPaintable)list.get(ii);
				p.paint(g,clip);
			}
		}


		/**
		 * @return
		 */
		public boolean getClipFlag()
		{
			return this.mClipFlag;
		}

		/**
		 * @param b
		 */
		public void setClipFlag(boolean b)
		{
			this.mClipFlag = b;
		}


		public SGImage getImage()
		{
			return this.mImage;
		}
		
		public SGTuple2f getImageSize()
		{
			return this.mImage.getImageSize();
		}
		
		public SGTuple2f getImageLocation()
		{
			return this.mImage.getImageLocation();
		}
		
		public boolean setImage(
			final Image img,
			final float x, final float y,
			final float w, final float h,
			final float factor )
		{
			this.mImage = new SGImage( img, this, x, y, w, h );
			this.mImage.setScalingFactor(factor);
			return true;
		}

	}



//
//	プロパティファイル関連
//





	/**
	 * 
	 * @param document
	 * @return
	 */
	public Element createElement( final Document document )
	{
		Element element = document.createElement( SGIRootObjectConstants.TAG_NAME_WINDOW );
		if( this.writeProperty( element ) == false )
		{
			return null;
		}
		return element;
	}
	
	
	/**
	 * 
	 * @param document
	 * @return
	 */
	public Element createElementForFocusedFiguresInBoundingBox( final Document document )
	{
		Element element = document.createElement( SGIRootObjectConstants.TAG_NAME_WINDOW );
		if( this.writePropertyForFocusedFiguresInBoundingBox( element ) == false )
		{
			return null;
		}
		return element;
	}


	

	/**
	 * Create a DOM Tree.
	 * @param document
	 * @return
	 */
	public boolean createDOMTree( Document document )
	{
		boolean flag;
		switch( this.mPropertyFileCreationModeOfFigures )
		{
			case ALL_FIGURES :
			{
				flag = this.createDOMTreeForAllFigures( document );
				break;
			}

			case FOCUSED_FIGURES_FOR_COPY :
			{
				flag = this.createDOMTreeForFocusedFiguresForDuplication( document );
				break;
			}

			case FOCUSED_FIGURES_IN_BOUNDING_BOX :
			{
				flag = this.createDOMTreeForFocusedFiguresInBoundingBox( document );
				break;
			}
			
			case FOCUSED_FIGURES_FOR_DUPLICATION :
			{
				flag = this.createDOMTreeForFocusedFiguresForDuplication( document );
				break;
			}
			
			default :
			{
				throw new Error();
			}
		}

		return flag;
	}
	
	
	/**
	 * Create a DOM Tree.
	 * @param document
	 * @param focused
	 * @return
	 */
	public boolean createDOMTree( Document document, int mode )
	{
		this.mPropertyFileCreationModeOfFigures = mode;
		return this.createDOMTree( document );
	}
	
	
	/**
	 * 
	 */
	private boolean createDOMTreeForAllFigures( Document document )
	{
		
		// get the root element
		Element property = document.getDocumentElement();
		
		// write properties of the window
		Element windowElement = this.createElement( document );
		if( windowElement==null )
		{
			return false;
		}
		property.appendChild( windowElement );
		
		// figures
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			Element el = figure.createElement( document );
			if( el == null )
			{
				return false;
			}
			windowElement.appendChild( el );
		}

		return true;
	}

	
	
	
	/**
	 * Creation mode of the property file of focused figures.
	 */
	private int mPropertyFileCreationModeOfFigures;



	
	/**
	 * 
	 * @return
	 */
	boolean createPropertyFileFromFocusedFigures()
	{
		this.mPropertyFileCreationModeOfFigures
			= FOCUSED_FIGURES_IN_BOUNDING_BOX;
		this.notifyToListener( MENUBARCMD_SAVE_PROPERTY );
		return true;
	}
	
	

	/**
	 * 
	 */
	private boolean createDOMTreeForFocusedFiguresInBoundingBox( Document document )
	{

		// get the root element
		Element property = document.getDocumentElement();

		
		// write properties of the window
		Element windowElement = this.createElementForFocusedFiguresInBoundingBox( document );
		if( windowElement==null )
		{
			return false;
		}
		property.appendChild( windowElement );

		
		// figures
		ArrayList list = this.getFocusedObjectsList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			Element el = figure.createElementForFocusedInBoundingBox( document );
			if( el == null )
			{
				return false;
			}
			windowElement.appendChild( el );
		}

		return true;
	}



	/**
	 * 
	 */
	private boolean createDOMTreeForFocusedFiguresForDuplication( Document document )
	{

		// get the root element
		Element property = document.getDocumentElement();

		
		// write properties of the window
		Element windowElement = this.createElement( document );
		if( windowElement==null )
		{
			return false;
		}
		property.appendChild( windowElement );

		
		// figures
		ArrayList list = this.getFocusedObjectsList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			Element el = figure.createElementForFocusedForDuplication( document );
			if( el == null )
			{
				return false;
			}
			windowElement.appendChild( el );
		}

		return true;
	}



	/**
	 * 
	 * @param element
	 * @return
	 */
	public boolean writePropertyForFocusedFiguresInBoundingBox( final Element element )
	{
		// Size
		Rectangle2D rect = this.getBoundingBoxOfFigures( this.getFocusedObjectsList() );
		final float width = (float)rect.getWidth()*SGIConstants.CM_POINT_RATIO/this.mMagnification;
		final float height = (float)rect.getHeight()*SGIConstants.CM_POINT_RATIO/this.mMagnification;

		element.setAttribute(
			KEY_PAPER_WIDTH,
			Float.toString( width )	+ SGIConstants.cm );
		element.setAttribute(
			KEY_PAPER_HEIGHT,
			Float.toString( height ) + SGIConstants.cm );
		
		this.writeProperty_(element);

		return true;
	}


	/**
	 * 
	 * @param document
	 * @param parent
	 * @return
	 */
	public boolean writeProperty( final Element element )
	{
		// Size
		element.setAttribute(
			KEY_PAPER_WIDTH,
			Float.toString( this.mClientPanel.getPaperWidth()*SGIConstants.CM_POINT_RATIO )
				+ SGIConstants.cm );
		element.setAttribute(
			KEY_PAPER_HEIGHT,
			Float.toString( this.mClientPanel.getPaperHeight()*SGIConstants.CM_POINT_RATIO )
			+ SGIConstants.cm );

		this.writeProperty_(element);

		return true;
	}

	
	
	private boolean writeProperty_( final Element element )
	{
		// Grid
		element.setAttribute(
			KEY_GRID_VISIBLE,
			Boolean.toString( this.mClientPanel.isGridLineVisible() ) );
		element.setAttribute(
			KEY_GRID_INTERVAL,
			Float.toString( this.mClientPanel.getGridLineInterval()*CM_POINT_RATIO )
				+ SGIConstants.cm );
		element.setAttribute(
			KEY_GRID_LINE_WIDTH,
			Float.toString( this.mClientPanel.getGridLineWidth() )
				+ SGIConstants.pt );

		// Color
		element.setAttribute(
			KEY_BACKGROUND_COLOR,
			SGUtilityText.getColorString( this.mClientPanel.getPaperColor() ) );
		element.setAttribute(
			KEY_GRID_COLOR,
			SGUtilityText.getColorString( this.mClientPanel.getGridLineColor() ) );

		return true;
	}
	
	
	
	/**
	 * 
	 * @param el
	 * @return
	 */
	public boolean readProperty( final Element el, final float min, final float max )
	{
		String str = null;
		Number num = null;
		Boolean b = null;
		Color cl = null;

		final float ratio = (max - min)/6;
		final SGIProgressControl progress = (SGIProgressControl)this;
		
		// width
		progress.setProgressValue( min );
		str = el.getAttribute( SGIRootObjectConstants.KEY_PAPER_WIDTH );
		if( str.length()!=0 )
		{
			StringBuffer uWidth = new StringBuffer();
			num = SGUtilityText.getNumber( str, uWidth );
			if( num==null )
			{
				return false;
			}
			final float width = num.floatValue();
			if( this.mClientPanel.setPaperWidth( width, uWidth.toString() ) == false )
			{
				return false;
			}
		}

		
		// height
		progress.setProgressValue( min + ratio * 1);
		str = el.getAttribute( SGIRootObjectConstants.KEY_PAPER_HEIGHT );
		if( str.length()!=0 )
		{
			StringBuffer uHeight = new StringBuffer();
			num = SGUtilityText.getNumber( str, uHeight );
			if( num==null )
			{
				return false;
			}
			final float height = num.floatValue();
			if( this.mClientPanel.setPaperHeight( height, uHeight.toString() ) == false )
			{
				return false;
			}
		}
		
		
		// grid visible
		progress.setProgressValue( min + ratio * 2);
		str = el.getAttribute( SGIRootObjectConstants.KEY_GRID_VISIBLE );
		if( str.length()!=0 )
		{
			b = SGUtilityText.getBoolean(str);
			if( b==null )
			{
				return false;
			}
			final boolean gridVisible = b.booleanValue();
			if( this.mClientPanel.setGridLineVisible( gridVisible ) == false )
			{
				return false;
			}
		}

		
		// grid interval
		progress.setProgressValue( min + ratio * 3);
		str = el.getAttribute( SGIRootObjectConstants.KEY_GRID_INTERVAL );
		if( str.length()!=0 )
		{
			StringBuffer uInterval = new StringBuffer();
			num = SGUtilityText.getNumber( str, uInterval );
			if( num==null )
			{
				return false;
			}
			final float interval = num.floatValue();
			if( this.mClientPanel.setGridLineInterval( interval, uInterval.toString() ) == false )
			{
				return false;
			}
		}
		
		
		// grid line width
		progress.setProgressValue( min + ratio * 4);
		str = el.getAttribute( SGIRootObjectConstants.KEY_GRID_LINE_WIDTH );
		if( str.length()!=0 )
		{
			StringBuffer uGridLineWidth = new StringBuffer();
			num = SGUtilityText.getNumber( str, uGridLineWidth );
			if( num==null )
			{
				return false;
			}
			final float gridLineWidth = num.floatValue();
			if( this.mClientPanel.setGridLineWidth( gridLineWidth, uGridLineWidth.toString() ) == false )
			{
				return false;
			}
		}
		
		
		// background color
		progress.setProgressValue( min + ratio * 5);
		str = el.getAttribute( SGIRootObjectConstants.KEY_BACKGROUND_COLOR );
		if( str.length()!=0 )
		{
			cl = SGUtilityText.getColorFromString( str );
			if( cl==null )
			{
				return false;
			}
			final Color bgColor = cl;
			if( this.mClientPanel.setPaperColor( bgColor ) == false )
			{
				return false;
			}
		}

		
		// grid line color
		progress.setProgressValue( min + ratio * 6);
		str = el.getAttribute( SGIRootObjectConstants.KEY_GRID_COLOR );
		if( str.length()!=0 )
		{
			cl = SGUtilityText.getColorFromString( str );
			if( cl==null )
			{
				return false;
			}
			final Color gridColor = cl;
			if( this.mClientPanel.setGridLineColor( gridColor ) == false )
			{
				return false;
			}
		}

		return true;
	}


//
// 共通プロパティ設定
//


	/**
	 * 
	 */
	boolean showPropertyDialogForSelectedFigures()
	{
		ArrayList figList = this.getFocusedObjectsList();
		ArrayList dList = new ArrayList();
		for( int ii=0; ii<figList.size(); ii++ )
		{
			SGFigure fig = (SGFigure)figList.get(ii);
			SGPropertyDialog dg = fig.getPropertyDialog();
			if( dg!=null )
			{
				dList.add(dg);
			}
		}

		// clear focused objects in figures
		List listAll = this.mFigureList;
		for( int ii=0; ii<listAll.size(); ii++ )
		{
			SGFigure fig = (SGFigure)listAll.get(ii);
			fig.clearFocusedObjects();
		}

		// add listeners to the property dialog		
		SGPropertyDialog dg = (SGPropertyDialog)dList.get(0);
		this.showPropertyDialog( dg, figList );

		return true;
	}





	/**
	 * 
	 * @return
	 */
	boolean showPropertyDialogForSelectedObjects(
		final SGFigure figure, final SGIFigureElement element )
	{
//System.out.println("<< setPropertyOfSelectedObjects >>");

		ArrayList obsList = new ArrayList();
		ArrayList dList = new ArrayList();
		Class cl = element.getClass();

		ArrayList figList = this.getVisibleFigureList();
		for( int ii=0; ii<figList.size(); ii++ )
		{
			SGFigure fig = (SGFigure)figList.get(ii);
			SGIFigureElement el = fig.getIFigureElement(cl);
			if( el==null )
			{
				continue;
			}
			ArrayList list = el.getPropertyDialogObserverList();
			if( list.size()==0 )
			{
				continue;
			}
			obsList.addAll(list);

			SGIPropertyDialogObserver obs = (SGIPropertyDialogObserver)obsList.get(0);
			SGPropertyDialog dg = obs.getPropertyDialog();
			dList.add(dg);
		}
		if( obsList.size()==0 )
		{
			return true;
		}

		// check dialogs
		for( int ii=0; ii<dList.size()-1; ii++ )
		{
			Object obj1 = dList.get(ii);
			for( int jj=ii; jj<dList.size(); jj++ )
			{
				Object obj2 = dList.get(jj);
				if( obj1.getClass().equals(obj2.getClass()) == false )
				{
					return false;
				}
			}
		}


		// clear focused objects
		for( int ii=0; ii<figList.size(); ii++ )
		{
			SGFigure fig = (SGFigure)figList.get(ii);
			fig.clearFocusedObjectsOtherThan( element );
			
			if( fig.equals(figure) == false )
			{
				fig.setSelected( false );
			}
		}
		this.getContentPane().repaint();


		// show the property dialog
		SGPropertyDialog dg = (SGPropertyDialog)dList.get(0);
		this.showPropertyDialog( dg, obsList );

		return true;
	}



	/**
	 * 
	 * @param dg
	 * @param l - a property dialog observer
	 */
	private void showPropertyDialog( SGPropertyDialog dg, SGIPropertyDialogObserver l )
	{
		ArrayList list = new ArrayList();
		list.add(l);
		this.showPropertyDialog( dg, list );
	}



	/**
	 * 
	 * @param dg
	 * @param lList - a list of property dialog observer
	 */
	private void showPropertyDialog( SGPropertyDialog dg, ArrayList lList )
	{
		for( int ii=0; ii<lList.size(); ii++ )
		{
			SGIPropertyDialogObserver l = (SGIPropertyDialogObserver)lList.get(ii);
			dg.addPropertyDialogObserver(l);
			l.prepare();
		}

		// set properties to dialog
		dg.setDialogProperty();
		dg.setLocation( this.getLocation() );


		// show property dialog
		dg.setVisible(true);

		dg.removeAllPropertyDialogObserver();

		//
		this.notifyToRoot();

	}


	private int mSavedListIndex = -1;

	/**
	 * Set guarantee that no information is lost by discarding this window.
	 * @param b
	 */
	public void setSaved( final boolean b )
	{
		if(b)
			this.mSavedListIndex = this.mUndoManager.getChangedObjectListIndex();
		this.updateStatusBarSavedFlag();
	}


	/**
	 * Whether it is guaranteed that no information is lost by discarding this window.
	 * @return
	 */
	public boolean isSaved()
	{
		return (this.mSavedListIndex==this.mUndoManager.getChangedObjectListIndex());
	}


	// interface implements of 'SGIWindowDialogObserver'
	public float getPaperWidth( final String unit )
	{
		return this.mClientPanel.getPaperWidth( unit );
	}
	
	public float getPaperHeight( final String unit )
	{
		return this.mClientPanel.getPaperHeight( unit );
	}
	
	public float getGridLineInterval( final String unit )	
	{
		return this.mClientPanel.getGridLineInterval( unit );
	}
	
	public float getGridLineWidth( final String unit )
	{
		return this.mClientPanel.getGridLineWidth( unit );
	}
	
	public boolean isGridLineVisible()
	{
		return this.mClientPanel.isGridLineVisible();
	}

	public Color getPaperColor()
	{
		return this.mClientPanel.getPaperColor();
	}

	public Color getGridLineColor()
	{
		return this.mClientPanel.getGridLineColor();
	}

	public float getImageLocationX()
	{
		return this.mClientPanel.getImageLocationX();
	}

	public float getImageLocationX( String unit )
	{
		return this.mClientPanel.getImageLocationX( unit );
	}

	public float getImageLocationY()
	{
		return this.mClientPanel.getImageLocationY();
	}

	public float getImageLocationY( String unit )
	{
		return this.mClientPanel.getImageLocationY( unit );
	}

	public float getImageWidth()
	{
		return this.mClientPanel.getImageWidth();
	}

	public float getImageWidth( String unit )
	{
		return this.mClientPanel.getImageWidth( unit );
	}

	public float getImageHeight()
	{
		return this.mClientPanel.getImageHeight();
	}

	public float getImageHeight( String unit )
	{
		return this.mClientPanel.getImageHeight( unit );
	}

	public float getImageScalingFactor()
	{
		return this.mClientPanel.getImageScalingFactor();
	}


	public boolean setPaperWidth( final float value, final String unit )
	{
		return this.mClientPanel.setPaperWidth( value, unit );
	}
	
	public boolean setPaperHeight( final float value, final String unit )
	{
		return this.mClientPanel.setPaperHeight( value, unit );
	}
	
	public boolean setGridLineInterval( final float value, final String unit )
	{
		return this.mClientPanel.setGridLineInterval( value, unit );
	}

	public boolean setGridLineWidth( final float value, final String unit )
	{
		return this.mClientPanel.setGridLineWidth( value, unit );
	}
	
	public boolean setGridLineVisible( final boolean b )
	{
		return this.mClientPanel.setGridLineVisible( b );
	}

	public boolean setPaperColor( final Color cl )
	{
		return this.mClientPanel.setPaperColor( cl );
	}
	
	public boolean setGridLineColor( final Color cl )
	{
		return this.mClientPanel.setGridLineColor( cl );
	}

	public boolean setImageLocationX( final float value, final String unit )
	{
		return this.mClientPanel.setImageLocationX( value, unit );
	}

	public boolean setImageLocationY( final float value, final String unit )
	{
		return this.mClientPanel.setImageLocationY( value, unit );
	}

	public boolean setImageWidth( final float value, final String unit )
	{
		return this.mClientPanel.setImageWidth( value, unit );
	}

	public boolean setImageHeight( final float value, final String unit )
	{
		return this.mClientPanel.setImageHeight( value, unit );
	}

	public boolean setImageScalingFactor( final float f )
	{
		return this.mClientPanel.setImageScalingFactor(f);
	}




//
// 内部クラス
//
	
	/**
	 * Property of SGDrawingWindow.
	 */
	public static class WindowProperties extends SGProperties
	{

		private ArrayList mVisibleFigureList = new ArrayList();
		private float mPaperWidth;
		private float mPaperHeight;
		private Color mBackgroundColor;
		private boolean mGridVisible;
		private Color mGridColor;
		private float mGridInverval;
		private float mGridLineWidth;
		private float mImageLocationX;
		private float mImageLocationY;
		private float mImageScalingFactor;
		private Image mImage;

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


		/**
		 * 
		 */
		public void dispose()
		{
			this.mVisibleFigureList.clear();
			this.mVisibleFigureList = null;
			
			this.mBackgroundColor = null;
			this.mGridColor = null;
			this.mImage = null;
		}


		/**
		 * 
		 */
		public boolean equals( final Object obj )
		{

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

			WindowProperties p = (WindowProperties)obj;

			if( p.mVisibleFigureList.equals(this.mVisibleFigureList) == false ) return false;
			if( p.mPaperWidth!=this.mPaperWidth ) return false;
			if( p.mPaperHeight!=this.mPaperHeight ) return false;
			if( p.mBackgroundColor.equals( this.mBackgroundColor ) == false ) return false;
			if( p.mGridVisible!=this.mGridVisible ) return false;
			if( p.mGridColor.equals( this.mGridColor ) == false ) return false;
			if( p.mGridInverval!=this.mGridInverval ) return false;
			if( p.mGridLineWidth!=this.mGridLineWidth ) return false;
			if( p.mImageLocationX!=this.mImageLocationX ) return false;
			if( p.mImageLocationY!=this.mImageLocationY ) return false;
			if( p.mImageScalingFactor!=this.mImageScalingFactor ) return false;
			if( p.mImage.equals(this.mImage) == false ) return false;

			return true;
		}


		public List getVisibleFigureList()
		{
			List list = new ArrayList( this.mVisibleFigureList );
			return list;
		}

		public Float getPaperWidth()
		{
			return new Float( this.mPaperWidth );
		}

		public Float getPaperHeight()
		{
			return new Float( this.mPaperHeight );
		}

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

		public Color getGridColor()
		{
			return this.mGridColor;
		}

		public Boolean getGridVisible()
		{
			return Boolean.valueOf( this.mGridVisible );
		}

		public Float getGridInterval()
		{
			return new Float( this.mGridInverval );
		}

		public Float getGridLineWidth()
		{
			return new Float( this.mGridLineWidth );
		}

		public Float getImageLocationX()
		{
			return new Float( this.mImageLocationX );
		}

		public Float getImageLocationY()
		{
			return new Float( this.mImageLocationY );
		}

		public Float getImageScalingFactor()
		{
			return new Float( this.mImageScalingFactor );
		}

		public Image getImage()
		{
			return this.mImage;
		}

		public void setVisibleFigureList( final List list )
		{
			if( list==null )
			{
				throw new IllegalArgumentException("list==null");
			}
			this.mVisibleFigureList = new ArrayList( list );
		}

		public void setPaperWidth( final float w )
		{
			if( w<0.0f )
			{
				throw new IllegalArgumentException("w<0.0f");
			}
			this.mPaperWidth = w;
		}

		public void setPaperHeight( final float h )
		{
			if( h<0.0f )
			{
				throw new IllegalArgumentException("h<0.0f");
			}
			this.mPaperHeight = h;
		}

		public void setBackGroundColor( final Color cl )
		{
			if( cl==null )
			{
				throw new IllegalArgumentException("cl==null");
			}
			this.mBackgroundColor = cl;
		}

		public void setGridColor( final Color cl )
		{
			if( cl==null )
			{
				throw new IllegalArgumentException("cl==null");
			}
			this.mGridColor = cl;
		}

		public void setGridVisible( final boolean b )
		{
			this.mGridVisible = b;
		}

		public void setGridInterval( final float num )
		{
			if( num<0.0f )
			{
				throw new IllegalArgumentException("num<0.0f");
			}
			this.mGridInverval = num;
		}

		public void setGridLineWidth( final float num )
		{
			if( num<0.0f )
			{
				throw new IllegalArgumentException("num<0.0f");
			}
			this.mGridLineWidth = num;
		}

		public boolean setImageLocationX( final float num )
		{
			this.mImageLocationX = num;
			return true;
		}

		public boolean setImageLocationY( final float num )
		{
			this.mImageLocationY = num;
			return true;
		}

		public boolean setImageScalingFactor( final float num )
		{
			if( num<0.0f )
			{
				throw new IllegalArgumentException("num<0.0f");
			}
			this.mImageScalingFactor = num;
			return true;
		}

		public boolean setImage( Image img )
		{
			this.mImage = img;
			return true;
		}
	}



}
