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

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.MediaTracker;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.math.BigDecimal;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.print.attribute.standard.MediaSize;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.event.MouseInputListener;

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




/**
 * 描画用ウインドウ
 */

public class SGDrawingWindow extends JFrame
	implements ComponentListener, PropertyChangeListener,
		MouseInputListener, MouseWheelListener, MenuListener,
		ActionListener, AdjustmentListener, WindowListener,
		SGIUndoable, SGIWindowDialogObserver, SGIRootObject, SGINode
{


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


	/**
	 * A layered pane.
	 */
	private final JLayeredPane mLayeredPane = new JLayeredPane();


	/**
	 * A panel to draw grid lines.<BR>
	 */
	private BackgroundPanel mBackgroundPanel;


	/**
	 * 
	 */
	private FigurePanel mFigurePanel;


	/**
	 * A panel to draw anchors.<BR>
	 */
	private ForegroundPanel mForegroundPanel;


	/**
	 * A panel to draw rulers.<BR>
	 */
	private RulerPanel mRulerPanel;


	/**
	 * 水平方向スクロールバー
	 */
	private JScrollBar mHScrollBar = null;


	/**
	 * 垂直方向スクロールバー
	 */
	private JScrollBar mVScrollBar = null;


	/**
	 * Popup menu.
	 */
	private JPopupMenu mPopupMenu = new JPopupMenu();


	/**
	 * Property dialog.
	 */
	private SGWindowDialog mDialog = null;


	/**
	 * 
	 */
	private SGIImageExportManager mImageExportManager;



	/**
	 * Temporary size of the viewport.
	 */
	private final SGTuple2f mTemporaryViewportSize = new SGTuple2f();



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



	/**
	 * 
	 */
	private SGToolBar mToolBar;



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






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


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


	/**
	 * 
	 */
	private final SGTuple2f mPaperSize = 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();


	/**
	 * コンストラクタ
	 */
	public SGDrawingWindow()
	{
		super();
		this.setTitle( TITLE );
		this.create();

		// set the close operation
		this.setDefaultCloseOperation( JFrame.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;
	}




	/**
	 * ウインドウの作成。<BR>
	 */
	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 pop-up menu
		this.createPopupMenu();


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


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


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


		// レイヤペインを追加
		this.getContentPane().add(
			mLayeredPane, BorderLayout.CENTER );


		this.createFigurePanel();


		// グリッド線描画用パネルを作成
		this.createBackgroundPanel();


		// アンカー描画用パネルを作成
		this.createForegroundPanel();


		// ルーラ描画用パネルを作成
		this.createRulerPanel();


		// スクロールバーを追加
		this.createScrollBars();


		// pack
		this.pack();


		// init flag map
		this.initInsertFlagMap();


		//
		// Add as an event listener
		//

		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		this.addMouseWheelListener(this);
		this.addWindowListener(this);


		//
		// update items
		//

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

		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_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 = SGDefaultValues.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 = SGDefaultValues.PAPER_WIDTH;
		final float height = SGDefaultValues.PAPER_HEIGHT;
		this.setPaperSizeRoundingOff( width, height );

		
		//
		this.updateClientRect();


		return true;
	}



	//
	public void repaintContentPane()
	{
//System.out.println("Window");
		this.getContentPane().repaint();
	}



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

		Container com = this.mForegroundPanel;
		JScrollBar vBar = new JScrollBar( JScrollBar.VERTICAL, 0, 100, 0, 100 );
		JScrollBar hBar = new JScrollBar( JScrollBar.HORIZONTAL, 0, 100, 0, 100 );
		vBar.setVisible(false);
		hBar.setVisible(false);

//java.awt.GridBagConstraints gridBagConstraints;
//com.setLayout(new java.awt.GridBagLayout());
//
//gridBagConstraints = new java.awt.GridBagConstraints();
//gridBagConstraints.gridx = 0;
//gridBagConstraints.gridy = 1;
//gridBagConstraints.gridwidth = 1;
//gridBagConstraints.weightx = 1.0;
//gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
//gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTH;
//com.add(hBar, gridBagConstraints);
//
//gridBagConstraints = new java.awt.GridBagConstraints();
//gridBagConstraints.gridx = 1;
//gridBagConstraints.gridy = 0;
//gridBagConstraints.gridheight = 1;
//gridBagConstraints.weighty = 1.0;
//gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
//gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
//com.add(vBar, gridBagConstraints);
//
//
//JPanel jPanel1 = new JPanel();
//jPanel1.setBackground(new java.awt.Color(255, 255, 255));
//gridBagConstraints = new java.awt.GridBagConstraints();
//gridBagConstraints.gridx = 1;
//gridBagConstraints.gridy = 1;
//gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
//com.add(jPanel1, gridBagConstraints);


		com.setLayout( new BorderLayout() );
		com.add( vBar, BorderLayout.EAST );
		com.add( hBar, BorderLayout.SOUTH );

		{
			final int min = hBar.getMinimum();
			final int max = hBar.getMaximum();
			final int extent = hBar.getVisibleAmount();
			hBar.setValue( (max - extent - min)/2 );
		}
		{
			final int min = vBar.getMinimum();
			final int max = vBar.getMaximum();
			final int extent = vBar.getVisibleAmount();
			vBar.setValue( (max - extent - min)/2 );
		}

		hBar.addAdjustmentListener(this);
		vBar.addAdjustmentListener(this);


		this.mHScrollBar = hBar;
		this.mVScrollBar = vBar;

		return true;
	}



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



	/**
	 * 
	 * @return
	 */
	private boolean createToolBar()
	{
		// ツールバーにコンポーネントを追加
		SGToolBar bar = new SGToolBar();
		this.mToolBar = bar;
		bar.setRoot( this );

//		String laf = SGUtility.getLookAndFeelID();
//		if( LAF_WINDOWS.equals(laf) )
//		{
//			bar.setRollover(true);
//		}

//		bar.setOrientation( JToolBar.HORIZONTAL );
//		bar.setFloatable( false );

		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 == JToolBar.HORIZONTAL )
		{
			this.getContentPane().add( this.mToolBar, BorderLayout.NORTH );
		}
		else if( ori == JToolBar.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();
	}


	/**
	 * 
	 * @return
	 */
	private boolean createBackgroundPanel()
	{
		BackgroundPanel p = new BackgroundPanel( this );
		mLayeredPane.add(p);
		mLayeredPane.setLayer( p, LAYER_GRID_PANEL );

		this.mBackgroundPanel = p;

		return true;
	}



	/**
	 * 
	 * @return
	 */
	private boolean createFigurePanel()
	{
		FigurePanel p = new FigurePanel( this );
		this.mLayeredPane.add(p);
		this.mLayeredPane.setLayer( p, LAYER_FIGURE );

		p.setLayout(null);

		this.mFigurePanel = p;

		return true;
	}


	/**
	 * 
	 */
	private boolean createForegroundPanel()
	{
		ForegroundPanel p = new ForegroundPanel( this );

		mLayeredPane.add( p, BorderLayout.CENTER );
		mLayeredPane.setLayer( p, LAYER_ANCHOR_PANEL );

		this.mForegroundPanel = p;

		return true;
	}



	/**
	 * 
	 * @return
	 */
	private boolean createRulerPanel()
	{
		RulerPanel p = new RulerPanel( this );
		this.mLayeredPane.add(p);
		this.mLayeredPane.setLayer( p, LAYER_RULER_PANEL );

		this.mRulerPanel = p;

		return true;
	}



	protected boolean mRulerVisibleFlag = true;

	public int getRulerWidth()
	{
		int width = 0;
		if( this.mRulerVisibleFlag )
		{
			width = RulerPanel.RULER_WIDTH;
		}
		return width;
	}



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

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

		SGUtility.addItem( p, this, MENUCMD_PASTE );

		p.addSeparator();

		SGUtility.addItem( p, this, MENUCMD_PROPERTY );

		return true;
	}




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




//
//	set/getメソッド
//


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



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


	/**
	 * 
	 * @return
	 */
	public boolean isGridLineVisible()
	{
		return this.mBackgroundPanel.isGridVisible();
	}


	/**
	 * 
	 */
	public float getGridLineWidth()
	{
		return this.mBackgroundPanel.getGridLineWidth();
	}


	/**
	 * 
	 * @return
	 */
	public float getGridLineInterval()
	{
		return this.mBackgroundPanel.getGridInterval();
	}


	/**
	 * 
	 */
	public Color getGridLineColor()
	{
		return this.mBackgroundPanel.getGridLineColor();
	}



	/**
	 * 
	 */
	public boolean setGridLineWidth( final float width )
	{
		this.mBackgroundPanel.setGridLineWidth( width );
		return true;
	}


	/**
	 * 
	 */
	public boolean setGridLineColor( final Color color )
	{
		this.mBackgroundPanel.setGridLineColor( color );
		return true;
	}


	/**
	 * 
	 */
	public boolean setPaperColor( final Color color )
	{
		this.mBackgroundPanel.setPaperColor( color );
		return true;
	}



	/**
	 * 
	 * @param b
	 * @return
	 */
	public boolean setGridLineVisible( final boolean b )
	{
		this.mBackgroundPanel.setGridVisible(b);
		return true;
	}


	/**
	 * 
	 */
	public boolean setGridLineInterval( final float interval )
	{
		this.mBackgroundPanel.setGridInterval( interval );
		return true;
	}



	/**
	 * 
	 */
	public Color getPaperColor()
	{
		return this.mBackgroundPanel.getPaperColor();
	}


	/**
	 * 
	 * @return
	 */
	static float getAnchorSize()
	{
		return ForegroundPanel.ANCHOR_SIZE;
	}


	/**
	 * 
	 * @return
	 */
	public int getCurrentFigureId()
	{
		return this.getFigureList().size() + 1;
	}

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



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

		// メニューバーの幅
		final JMenuBar menuBar = this.getJMenuBar();
		final int menuBarWidth = menuBar.getHeight();

		// ツールバーの幅
		int tHeight = 0;
		if( this.mToolBar.getOrientation() == JToolBar.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;
		
//		final int toolBarWidth = this.mBottomToolBar.getHeight();
//		final int width = iBottom + toolBarWidth;
//		return width;

		return iBottom;
	}



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

		int tWidth = 0;
		if( this.mToolBar.getOrientation() == JToolBar.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 ArrayList mFigureList = new ArrayList();


	/**
	 * Returns a figure with given ID.
	 * @param id - Figure ID
	 * @return figure with given ID
	 */
	public SGFigure getFigure( final int id )
	{
		ArrayList 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()
	{
		ArrayList 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 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;
		}
		return this.hideFigure(f);
	}


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



	/**
	 * 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
	 * @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.getLocationOnFigure(pos);
		figure.setGraphRectLocation(
			(float)location.getX(), (float)location.getY()
		);

		// snap to the lines
		figure.snapToLines( SGFigure.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;
	}




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



	/**
	 * 
	 * @param figure
	 * @return
	 */
	public boolean drawBackFigure( final SGFigure figure )
	{
		return figure.drawbackFigure();
	}



	/**
	 * 
	 * @return
	 */
	public JComponent getFigurePanel()
	{
		return this.mFigurePanel;
	}



	/**
	 * Returns a list of child nodes.
	 * @return a list of chid 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;
	}


	/**
	 * 
	 * @param x	X-coordinate on the window.
	 * @param y	Y-coordinate on the window.
	 * @return
	 */
	public Point2D getLocationOnFigure( final int x, final int y )
	{
		final int rw = this.getRulerWidth();
		final float xx = x - this.getLeftWidth() - rw;
		final float yy = y - this.getTopWidth() - rw;
		return new Point2D.Float(xx,yy);
	}



	/**
	 * 
	 * @param pos
	 * @return
	 */
	public Point2D getLocationOnFigure( final Point pos )
	{
		return this.getLocationOnFigure( pos.x, pos.y );
	}





//
// スクロールバー関連
//

	/**
	 * 
	 */
	protected boolean setScrollValue(
		final JScrollBar bar, final double ratio )
	{

		final int min = bar.getMinimum();
		final int max = bar.getMaximum();
		final int extent = bar.getVisibleAmount();

		final int num = max - min - extent;		
		final double value = ratio*num;

		bar.setValue( (int)value );

		return true;
	}



	/**
	 * スクロールバーの値を取得
	 */
	protected float getScrollValue( final JScrollBar bar )
	{

		final int min = bar.getMinimum();
		final int max = bar.getMaximum();
		final int extent = bar.getVisibleAmount();

		if( max - min -extent == 0 )
		{
			return 0.0f;
		}

		final float ratio = (float)bar.getValue()/(float)(max-min-extent);

		return ratio;
	}



	/**
	 * 水平スクロールバーの値を取得
	 */
	protected float getHScrollValue()
	{
		return this.getScrollValue( this.mHScrollBar );
	}


	/**
	 * 垂直スクロールバーの値を取得
	 */
	protected float getVScrollValue()
	{
		return this.getScrollValue( this.mVScrollBar );
	}



	/**
	 * 
	 */
/*	private boolean setScrollBarProperty()
	{
//System.out.println("<<< setScrollBarProperty >>>");

		// スクロールバーから情報を取得
		final int vMax = this.mVScrollBar.getMaximum();
		final int vMin = this.mVScrollBar.getMinimum();
		final int hMax = this.mHScrollBar.getMaximum();
		final int hMin = this.mHScrollBar.getMinimum();
		final int vValueOld = this.mVScrollBar.getValue();
		final int hValueOld = this.mHScrollBar.getValue();
		final int vExtentOld = this.mVScrollBar.getVisibleAmount();
		final int hExtentOld = this.mHScrollBar.getVisibleAmount();


		// 現在のvalueの[min,max-extent]区間での比を求める
		final double vRatio = (double)( vValueOld - vMin )/(double)( vMax - vExtentOld - vMin );
		final double hRatio = (double)( hValueOld - hMin )/(double)( hMax - hExtentOld - hMin );


		// スクロールバーのノブの長さの設定
		final int vExtent = (int)( (double)(vMax-vMin)/this.mMagnification );
		final int hExtent = (int)( (double)(hMax-hMin)/this.mMagnification );


		this.mVScrollBar.setVisibleAmount(vExtent);
		this.mHScrollBar.setVisibleAmount(hExtent);


		//
		// valueの設定
		//

		final int vValue = (int)( vMin + vRatio*(vMax-vExtent-vMin) );
		final int hValue = (int)( hMin + hRatio*(hMax-hExtent-hMin) );
		this.mVScrollBar.setValue(vValue);
		this.mHScrollBar.setValue(hValue);


//System.out.println(vValue+"  "+hValue);
//System.out.println(vExtent+"  "+hExtent);

//System.out.println(this.mVScrollBar.getVisibleAmount());
//System.out.println(this.mHScrollBar.getVisibleAmount());
//System.out.println();


		return true;
	}
*/


	/**
	 * 
	 * @return
	 */
	private boolean setScrollBarValue()
	{
//System.out.println("<< setScrollBarValue >>");

		if( this.mHScrollBar.isVisible() )
		{
			this.setScrollBarValue( true );
		}

		if( this.mVScrollBar.isVisible() )
		{
			this.setScrollBarValue( false );
		}

		return true;
	}



	private boolean setScrollBarValue( final boolean flag )
	{
		Rectangle2D cRect = this.getClientRect();
		Rectangle2D vpRect = this.getViewportBounds();

		JScrollBar bar = null;
		float cLength = 0.0f;
		float cStart = 0.0f;
		float vpLength = 0.0f;

		if( flag )
		{
			bar = this.mHScrollBar;
			cLength = (float)cRect.getWidth();
			cStart = (float)cRect.getX();
			vpLength = (float)vpRect.getWidth();
		}
		else
		{
			bar = this.mVScrollBar;
			cLength = (float)cRect.getHeight();
			cStart = (float)cRect.getY();
			vpLength = (float)vpRect.getHeight();
		}

		final int min = bar.getMinimum();
		final int max = bar.getMaximum();
		final int extent = (int)( (vpLength/cLength)*( max - min ) );
		bar.setVisibleAmount( extent );

		final float ratio = - cStart/( cLength - vpLength );
//System.out.println("ratio="+ratio);
		final int value = min + (int)( ratio*( max - extent - min ) );
		bar.setValue(value);

		// set the block increment to be equal to the extent
		bar.setBlockIncrement( extent );

		return true;
	}






//
// ウインドウ内領域関係
//



	/**
	 * ビューポートのサイズを取得する
	 */
	public SGTuple2f getViewportSize()
	{
		final SGTuple2f size = this.getPaneSize();
		final int rw = this.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()
	{

		// ボーダ領域の寸法を取得
		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;

		// メニューバーの幅
		final JMenuBar menuBar = this.getJMenuBar();
		final int menuBarWidth = menuBar.getHeight();

		// ツールバーの幅
		final int toolBarWidth = this.getToolBarHeight();

		// サイズを設定
		final float sizeX = this.getWidth() - ( iLeft + iRight );
		final float sizeY = this.getHeight() - (
			iTop + iBottom + menuBarWidth + toolBarWidth );

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

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

		return rect;
	}


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



	/**
	 * 
	 */
	public boolean setPaperWidth( final float w )
	{
		return this.setPaperSize( w, this.getPaperHeight() );
	}


	/**
	 * 
	 */
	public boolean setPaperHeight( final float h )
	{
		return this.setPaperSize( this.getPaperWidth(), h );
	}



	/**
	 * 
	 * @param w
	 * @param h
	 * @return
	 */
	public boolean setPaperSize( final float w, final float h )
	{
		return this.setPaperSizeRoundingOff( w, h );
	}



	/**
	 * 
	 * @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 float getPaperWidth()
	{
		return this.mPaperSize.x;
	}


	/**
	 * 
	 * @return
	 */
	public float getPaperHeight()
	{
		return this.mPaperSize.y;
	}




	/**
	 * 
	 * @return
	 */
	public Rectangle2D getPaperRect()
	{
		final float mag = this.getMagnification();
		Rectangle2D rect = new Rectangle2D.Float(
			this.getPaperX(), this.getPaperY(),
			mag*this.getPaperWidth(), mag*this.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
		);

//		if( this.getVisibleFigureListFromMap().size()!=0 )
//		{
//			Rectangle2D fRect = this.getBoundingBoxOfFigures( this.getVisibleFigureListFromMap() );
//			if( fRect!=null )
//			{
//				rect = rect.createUnion(fRect);
//			}
//		}

		return rect;
	}



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

		this.mTemporaryViewportSize.setValues( width, height );


		// ボーダ領域の寸法を取得
		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;

//System.out.println("insets:"+insets);
//System.out.println();

		// メニューバーの幅
		final JMenuBar menuBar = this.getJMenuBar();
		final int menuBarWidth = menuBar.getHeight();

//System.out.println("menuBar:"+menuBar);
//System.out.println();


		// ツールバーの幅
		final int toolBarWidth = this.getToolBarHeight();

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


		// サイズを設定
		final int rw = this.getRulerWidth();
		final float sizeX = width + iLeft + iRight + rw;
		final float sizeY = height + iTop + iBottom
			+ menuBarWidth + toolBarWidth + rw;
		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();
		final int rw = this.getRulerWidth();

		this.mLayeredPane.setSize( width, height );
		this.mRulerPanel.setBounds( 0, 0, width, height );
		this.mForegroundPanel.setBounds( rw, rw, width - rw, height - rw );
		this.mForegroundPanel.setPreferredSize( new Dimension( width - rw, height - rw ) );
		this.mForegroundPanel.validate();
		this.mBackgroundPanel.setBounds( rw, rw, width - rw, height - rw );
		this.mFigurePanel.setBounds( rw, rw, width - rw, height - rw );

		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
		SGTuple2f vpSize = this.getViewportSize();
		ArrayList list = this.getVisibleFigureList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.recordFigureRect();
//			figure.setSize( vpSize );
			figure.setViewBounds();
		}


		// when the figures are locked
		if( this.mLockFigureFlag )
		{

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



	/**
	 * 
	 * @param mag
	 * @return
	 */
	private 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 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.mRulerPanel.setMagnification( cl );
		this.mBackgroundPanel.setMagnification( cl );
		this.mForegroundPanel.setMagnification( cl );
		this.mFigurePanel.setMagnification( cl );


		//
		this.updateClientRect();

		//
//		this.setScrollBarValue();


		this.getContentPane().repaint();


		return true;
	}




	/**
	 * 
	 */
	public boolean setFigureBoundingBox( final int mode )
	{
//System.out.println("<< setFigureBoundingBox >>");
//System.out.println("mode="+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.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 void adjustmentValueChanged(
		final AdjustmentEvent e)
	{
//System.out.println("adjustmentValueChanged");
//System.out.println(e);

		Object source = e.getSource();

		if( source instanceof JScrollBar )
		{

			JScrollBar bar = (JScrollBar)source;

			// クライアント領域の位置の設定
			if( bar.equals( this.mHScrollBar ) )
			{
				this.setClientRectByValueOfScrollBar( true );
			}
			else if( bar.equals( this.mVScrollBar ) )
			{
				this.setClientRectByValueOfScrollBar( false );
			}

			//
			this.updateGraphRectOfAllFigures();

			this.getContentPane().repaint();
		}

	}



	/**
	 * 
	 * @param flag
	 * @return
	 */
	private boolean setClientRectByValueOfScrollBar(
		final boolean flag )
	{

		JScrollBar bar = null;
		if( flag )
		{
			bar = this.mHScrollBar;
		}
		else
		{
			bar = this.mVScrollBar;
		}

		final float value = this.getScrollValue( bar );

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


		Rectangle2D cRect = this.getClientRect();
		Rectangle2D vpRect = this.getViewportBounds();

		final float cx = (float)cRect.getX();
		final float cy = (float)cRect.getY();
		final float cWidth = (float)cRect.getWidth();
		final float cHeight = (float)cRect.getHeight();
		final float vpWidth = (float)vpRect.getWidth();
		final float vpHeight = (float)vpRect.getHeight();

		if( flag )
		{
			final float diff = cWidth - vpWidth;
			final float x = - value*diff;
			this.setClientRect(
				x, cy, cWidth, cHeight
			);
		}
		else
		{
			final float diff = cHeight - vpHeight;
			final float y = - value*diff;
			this.setClientRect(
				cx, y, cWidth, cHeight
			);
		}

		return true;
	}



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

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

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

		return com;
	}






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


	/**
	 * バウンドプロパティの変更時に呼び出されます。
	 */
	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();

//System.out.println(source);
//System.out.println(pName);
//System.out.println(oldValue+"  "+newValue);
//System.out.println();

		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.getRulerWidth();
	}


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



	/**
	 * コンポーネント上でマウスボタンをクリック (押してから離す) したときに呼び出されます。
	 */
	public void mouseClicked( final MouseEvent e )
	{
		// clear focus for all components
		SGUtility.clearGlobalFocusOwner();

		final int ls = this.getLeftShift();
		final int ts = this.getTopShift();
		e.translatePoint( -ls, -ts );
		final boolean b = this.clickFigures(e);
		e.translatePoint( ls, ts );
		if(!b)
		{
			this.onMouseClicked(e);
		}
		this.updateDataItem();
		this.updateFocusedObjectItem();
	}


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

		// clear all focused figures and focused objects in all figures
		this.clearAllFocusedObjectsInFigures();


		// set the default cursor
		this.setDefaultCursor();


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


		// マウス右ボタンクリックでポップアップメニューを出す
		if( (SwingUtilities.isRightMouseButton(e) )
			&& ( e.getClickCount() == 1 ) )
		{
			mPopupMenu.show( this, e.getX(), e.getY() );
		}


		return true;

	}


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



	/**
	 * 
	 */
	protected boolean clickFigures( final MouseEvent e )
	{
		ArrayList list = this.getVisibleFigureList();
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			if( figure.onMouseClicked(e) )
			{
				return true;
			}

		}
		return false;
	}




	private static final String DIALOG_TITLE_ADDITION_ERROR
		= "Failed to add a symbol.";

	private static final String ERRMSG_CLICK_GRAPH_RECTANGLE
		= "Click within the recatngle of figure.";



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

		ArrayList list = this.getVisibleFigureList();
		if( list.size()==0 )
		{
			return true;
		}


		final boolean b = this.isInsertFlagSelected();		
		if(b)
		{

			String msg = null;
			for( int ii=list.size()-1; ii>=0; ii-- )
			{
				SGFigure figure = (SGFigure)list.get(ii);
				figure.onMouseReleased(e);

				// add symbols to the figure by the toggle buttons
				if( this.insertSymbol( figure, x, y ) )
				{
					// set the default cursor
					this.setDefaultCursor();

					// set insert items unselected
					this.setInsertToggleItemsUnselected();

					return true;
				}
			}


			// show an error message dialog
			SGUtility.showErrorMessageDialog(
				this,
				ERRMSG_CLICK_GRAPH_RECTANGLE,
				DIALOG_TITLE_ADDITION_ERROR
			);

		}
		else
		{

			// notify the mouse release to figures
			for( int ii=0; ii<list.size(); ii++ )
			{
				SGFigure figure = (SGFigure)list.get(ii);
				figure.onMouseReleased(e);
			}

			
			// 履歴に残す
			ArrayList fList = this.getFocusedObjectsList();
			for( int ii=0; ii<fList.size(); ii++ )
			{
				SGFigure figure = (SGFigure)fList.get(ii);
				if( figure.isFigureMoved() )
				{
					figure.setChanged(true);
				}
			}

			fList = this.getVisibleFigureList();
			for( int ii=0; ii<fList.size(); ii++ )
			{
				SGFigure figure = (SGFigure)fList.get(ii);
				SGIFigureElement[] array = figure.getIFigureElementArray();
				for( int jj=array.length-1; jj>=0; jj-- )
				{
					array[jj].setChangedFocusedObjects();
				}
			}
			
			// update the client rectangle
			this.updateClientRect();
//			this.setScrollBarValue();

			// repaint
			this.repaintContentPane();

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


		return true;
	}



	// insert a symbol to figure
	private 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 siginificant difference
		if( this.getSignificantDifferenceSymbolInsertionFlag() )
		{
			flag = figure.addSignificantDifferenceSymbol(x,y);
		}

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

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

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

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

		return flag;
	}



	/**
	 * 
	 */
	protected boolean pressFigures( final MouseEvent e )
	{
		ArrayList list = this.getVisibleFigureList();
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			SGFigure figure = (SGFigure)list.get(ii);

			if( figure.onMousePressed(e) )
			{
				return true;
			}
		}

		return false;
	}





	/**
	 * 
	 */
	protected void onMouseDraggedOnFigures( final MouseEvent e )
	{
		// if insertion toggle button is selected
		if( this.isInsertFlagSelected() )
		{
			return;
		}

		// if the right button is pressed, there is nothing to do
		// for the mouse drag event
		if( SwingUtilities.isRightMouseButton(e) )
		{
			return;
		}

		ArrayList list = this.getVisibleFigureList();
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			if( figure.onMouseDragged(e) == true )
			{
				this.repaintContentPane();
				return;
			}
		}

	}


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



	/**
	 * コンポーネントにマウスが入ると呼び出されます。 
	 */
	public void mouseEntered(final MouseEvent e){}

	/**
	 * コンポーネントからマウスが出ると呼び出されます。 
	 */
	public void mouseExited(final MouseEvent e){}

	
	private boolean mViewportPressedFlag = false;

	/**
	 * コンポーネント上でマウスボタンを押すと呼び出されます。 
	 */
	public void mousePressed( final MouseEvent e )
	{

		// Only when the mouse is pressed in the pane,
		// notify the mouse event to the figures
		if( this.getViewportBoundsInComponent().contains( e.getPoint() ) )
		{
			this.mViewportPressedFlag = true;
		}
		else
		{
			return;
		}


		//
		// for ruler panel
		//

		{
			final int ls = this.getLeftWidth();
			final int ts = this.getTopWidth();
			e.translatePoint( -ls, -ts );
		
			if( this.mRulerPanel.pressed(e) )
			{
				return;
			}
		
			e.translatePoint( ls, ts );
		}


		//
		// for figures
		//

		{
			final int ls = this.getLeftShift();
			final int ts = this.getTopShift();
			e.translatePoint( -ls, -ts );
		
			// record the pressed point
			this.mTempMouseLocation.setLocation( e.getPoint() );

			if( this.pressFigures(e) )
			{
				return;
			}
		
			e.translatePoint( ls, ts );
		}

		//
		this.onMousePressed(e);

	}



	/**
	 * 
	 */

	protected final Point mTempMouseLocation = new Point();



	/**
	 * 
	 */
	protected boolean onMousePressed( final MouseEvent e )
	{
		this.clearAllFocusedObjectsInFigures();
		return true;
	}



	/**
	 * コンポーネント上でマウスボタンを離すと呼び出されます。 
	 */
	public void mouseReleased(final MouseEvent e)
	{
		// Only when the mouse is pressed in the viewport,
		// notify the mouse event to the figures

		if( this.mViewportPressedFlag == false )
		{
			return;
		}		
		
		
		//
		// for ruler panel
		//

		if( this.mRulerPanel.isPressed() )
		{
			final int ls = this.getLeftWidth();
			final int ts = this.getTopWidth();
			e.translatePoint( -ls, -ts );
			this.mRulerPanel.released(e);
			e.translatePoint( ls, ts );
			this.repaintContentPane();
		}
		else
		{
			final int ls = this.getLeftShift();
			final int ts = this.getTopShift();
			e.translatePoint( -ls, -ts );
			this.onMouseReleasedToFigure(e);
			e.translatePoint( ls, ts );
		}


		// set the flag
		this.mViewportPressedFlag = false;

	}



	/**
	 * コンポーネント上でマウスのボタンを押してドラッグすると呼び出されます。 
	 */
	public void mouseDragged( final MouseEvent e )
	{
//System.out.println(e);

		// Only when the mouse is pressed in the viewport,
		// notify the mouse event to the figures
		if( this.mViewportPressedFlag == false )
		{
			return;
		}

		//
		// for ruler panel
		//

		if( this.mRulerPanel.isPressed() )
		{
			final int ls = this.getLeftWidth();
			final int ts = this.getTopWidth();
			e.translatePoint( -ls, -ts );
			this.mRulerPanel.dragged(e);
			e.translatePoint( ls, ts );
			this.repaintContentPane();
		}
		else
		{
			// notify to figures
			final int ls = this.getLeftShift();
			final int ts = this.getTopShift();
			e.translatePoint( -ls, -ts );
			this.onMouseDraggedOnFigures(e);
			e.translatePoint( ls, ts );
		}

		// call handler method of this window
		this.onMouseMovedOrDragged(e);

	}



	/**
	 * コンポーネント上で、ボタンを押さずにマウスを移動すると呼び出されます。 
	 */
	public void mouseMoved(final MouseEvent e)
	{

		final int ls = this.getLeftShift();
		final int ts = this.getTopShift();
		e.translatePoint( -ls, -ts );

		ArrayList list = this.getVisibleFigureList();
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			if( figure.onMouseMoved(e) )
			{
				return;
			}
		}

		e.translatePoint( ls, ts );

		this.onMouseMovedOrDragged(e);

	}



	/**
	 * 
	 */
	private void onMouseMovedOrDragged( final MouseEvent e )
	{
		// マウスが取得する座標は、タイトルバー・メニューバー・ツールバー・インセット
		// を含む全領域における座標値である。

		Point p = this.getLocationInPane( e.getX(), e.getY() );
		final int x = p.x;
		final int y = p.y;


		if( x<0 || y<0 )
		{
			return;
		}


		// 位置情報ラベルへの設定
		this.setPositionLabel( x, y );

	}



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


		// ボーダ領域の寸法を取得
		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;


		// メニューバー
		final JMenuBar menuBar = this.getJMenuBar();
		final double menuHeight = menuBar.getHeight();
		yy -= menuHeight;


		// ツールバー
		yy -= this.getToolBarHeight();


		// ルーラ
		final double rulerWidth = this.getRulerWidth();
		xx -= rulerWidth;
		yy -= rulerWidth;

//System.out.println(xx+"  "+yy);

		return new Point(xx,yy);
	}



	/**
	 * ウインドウに所属するコンポーネントからの、位置情報ラベルへの設定
	 * @param com ウインドウに所属するコンポーネント
	 * @param x x座標値
	 * @param y y座標値
	 * @return true:成功、false:失敗
	 */
	protected boolean notifyPosition( final Component com, final int x, final int y )
	{
		// ボーダ領域の寸法を取得
		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;

//System.out.println(x+" "+y);

		// 子コンポーネント上での座標値から、ペイン上での座標値に変換
		int xx = x + com.getX();
		int yy = y + com.getY();

		yy += mTop;


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


		// ruler
		final int rw = this.getRulerWidth();
		xx -= rw;
		yy -= rw;


		if( xx<0 || yy<0 )
		{
			return true;
		}


		// 位置情報ラベルへの設定
		this.setPositionLabel( xx, yy );

		return true;

	}


	/**
	 * 
	 */
	public static final String WINDOW_TITLE_PREFIX = "Samurai Graph - Window : ";


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

		final DecimalFormat df = new DecimalFormat("0.0");

		final float ratio = CM_POINT_RATIO/this.mMagnification;
		float xx = (-cx+x)*ratio;
		float yy = (-cy+y)*ratio;
		final String strPos = "(" + df.format(xx) + ", " + df.format(yy) + ")";

		final String title = WINDOW_TITLE_PREFIX + this.getID() + "  " + strPos;
		this.setTitle( title );

		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) )
			{
				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 void mouseWheelMoved(MouseWheelEvent e)
	{
//System.out.println(e);

		// scroll the vertical scroll bar
		JScrollBar bar = this.mVScrollBar;
		final int amount = e.getScrollAmount()*e.getWheelRotation();
		final int value = bar.getValue() + amount;
		bar.setValue( value );

	}



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



	/**
	 * 
	 * @param flag
	 * @return
	 */
	private 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();

	}



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



	/**
	 * 
	 * @return
	 */
	private 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 );
		
		Component[] array = this.mPopupMenu.getComponents();
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii] instanceof JMenuItem )
			{
				JMenuItem item = (JMenuItem)array[ii];
				if( item.getActionCommand().equals( MENUBARCMD_PASTE ) )
				{
					item.setEnabled(b);
				}
			}
		}
	}

	
	
	/**
	 * 
	 *
	 */
	private void updateGridItems()
	{
		final boolean gridVisible = this.isGridLineVisible();
		boolean plusFlag = false;
		boolean minusFlag = false;
		if( gridVisible )
		{
			final float interval = this.getGridLineInterval()*CM_POINT_RATIO;
			final float min = SGDrawingWindow.GRID_INTERVAL_MIN_VALUE;
			final float max = SGDrawingWindow.GRID_INTERVAL_MAX_VALUE;
			final float step = SGDrawingWindow.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 boolean mPaperPortraitFlag = true;


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


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



	/**
	 * 
	 *
	 */
	private void updatePaperItems()
	{
		final boolean b = this.mPaperPortraitFlag;
		this.mMenuBar.setMenuItemSelected(
			MENUBAR_LAYOUT, MENUBARCMD_PAPER_PORTRAIT, b );
	}


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



	/**
	 * 
	 *
	 */	
	private void updateZoomItems()
	{
		final int[] array = SGDrawingWindow.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++ )
		{
//System.out.println(ii+"  "+keys[ii]+"  "+ tBar.isToolBarVisible( keys[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 );
//			this.closeWindow();
		}
		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_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_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_PAPER_A4_SIZE )
			|| command.equals( MENUBARCMD_PAPER_B5_SIZE )
			|| command.equals( MENUBARCMD_PAPER_USLETTER_SIZE ) )
		{

			MediaSize size = null;
			if( command.equals( MENUBARCMD_PAPER_A4_SIZE ) )
			{
				size = MediaSize.ISO.A4;
			}
			else if( command.equals( MENUBARCMD_PAPER_B5_SIZE ) )
			{
				size = MediaSize.ISO.B5;
			}
			else if( command.equals( MENUBARCMD_PAPER_USLETTER_SIZE ) )
			{
				size = MediaSize.NA.LETTER;
			}

			this.onPaperSizeChanged( size );
		}
		else if( command.equals( MENUBARCMD_PAPER_PORTRAIT )
			|| command.equals( MENUBARCMD_PAPER_LANDSCAPE ) )
		{
			this.mPaperPortraitFlag = ( command.equals(MENUBARCMD_PAPER_PORTRAIT) );
			this.updatePaperItems();
		}
		else if( command.equals( MENUBARCMD_BOUNDING_BOX ) )
		{
			this.setBoundingBox();
		}
		else if( command.equals( MENUBARCMD_PAPER_USER_CUSTOMIZE ) )
		{
			this.showPropertyDialog();
		}
		else if( command.equals( MENUBARCMD_AUTO_ARRANGEMENT ) )
		{
			this.onFiguresAligned();
		}
		else if( command.equals( MENUBARCMD_GRID_VISIBLE ) )
		{
			this.setGridLineVisible( !this.isGridLineVisible() );
			this.updateGridItems();
			this.getContentPane().repaint();

			this.setChanged(true);
			this.notifyToRoot();
		}
		else if( command.equals( MENUBARCMD_PLUS_GRID ) )
		{
			final float value = this.getGridLineInterval()*CM_POINT_RATIO;
			final float min = SGDrawingWindow.GRID_INTERVAL_MIN_VALUE;
			final float max = SGDrawingWindow.GRID_INTERVAL_MAX_VALUE;
			final float step = SGDrawingWindow.GRID_INTERVAL_STEP_SIZE;
			float 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.setGridLineInterval( valueNew/SGIConstants.CM_POINT_RATIO );
				this.updateGridItems();
				this.getContentPane().repaint();

				this.setChanged(true);
				this.notifyToRoot();
			}
		}
		else if( command.equals( MENUBARCMD_MINUS_GRID ) )
		{
			final float value = this.getGridLineInterval()*CM_POINT_RATIO;
			final float min = SGDrawingWindow.GRID_INTERVAL_MIN_VALUE;
			final float max = SGDrawingWindow.GRID_INTERVAL_MAX_VALUE;
			final float step = SGDrawingWindow.GRID_INTERVAL_STEP_SIZE;
			float 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.setGridLineInterval( 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 = SGDrawingWindow.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 = SGDrawingWindow.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.mLockFigureFlag = !this.mLockFigureFlag;
			this.updateLockItems();
		}
		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.setDefaultCursor();
			}
		}



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

	}




//
// Handler methods of WindowEvent
//

	public void windowActivated( WindowEvent e )
	{
//System.out.println(e);
	}

	public void windowDeactivated( WindowEvent e )
	{
//System.out.println(e);
	}

	public void windowOpened( WindowEvent e )
	{
//System.out.println(e);
	}

	public void windowClosed( WindowEvent e )
	{
//System.out.println(e);
	}

	public void windowClosing( WindowEvent e )
	{
//System.out.println(e);
	}

	public void windowIconified( WindowEvent e )
	{
//System.out.println(e);
	}

	public void windowDeiconified( WindowEvent e )
	{
//System.out.println(e);
	}



	/**
	 * 
	 *
	 */
	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();
		ArrayList objList = this.mFigureList;

		ArrayList objListOld = new ArrayList( objList );

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

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

		this.notifyToRoot();
		this.updateDataItem();
		this.repaintContentPane();
	}


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

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

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


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


	/**
	 * 
	 *
	 */
	void pasteCopiedObjects()
	{
		this.notifyToListener( MENUBARCMD_PASTE );

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


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


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




	/**
	 * 
	 *
	 */
	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 boolean mAutoZoomFlag = false;


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


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

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

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

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



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





//
// About component bounds
//

	
	/**
	 * 
	 * @return
	 */
	private Rectangle2D getPaperRectInClientRect()
	{
		Rectangle2D rect = new Rectangle2D.Float(
			this.mPaperOrigin.x, this.mPaperOrigin.y,
			this.mPaperSize.x, this.mPaperSize.y
		);
		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);
	}


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

		// 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
	 */
	private boolean onPaperSizeChanged( final MediaSize size )
	{

		// record the previous size
		Rectangle pRect = this.getPaperRect().getBounds();


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


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


		//
		this.updateClientRect();
//		this.setScrollBarValue();

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





	/**
	 * Set the size of window with given media size.
	 */
	public boolean setPaperSize( MediaSize size )
	{
		if( size==null )
		{
			return false;
		}

		// length in units of cm
		BigDecimal bdWidthCM = new BigDecimal( size.getX( MediaSize.MM ) );
		BigDecimal bdHeightCM = new BigDecimal( size.getY( MediaSize.MM ) );
		bdWidthCM = bdWidthCM.movePointLeft(1);
		bdHeightCM = bdHeightCM.movePointLeft(1);
		final float widthCM = bdWidthCM.floatValue();
		final float heightCM = bdHeightCM.floatValue();

		// set size
		if( this.mPaperPortraitFlag )
		{
			this.setPaperSizeRoundingOffInCMUnit( widthCM, heightCM );
		}
		else
		{
			this.setPaperSizeRoundingOffInCMUnit( heightCM, widthCM );
		}

		return true;
	}



	private boolean setPaperSizeRoundingOffInCMUnit(
		final float widthCM, final float heightCM )
	{
		// round out the size
		final float dWidthCM = (float)SGUtilityNumber.roundOffNumber( widthCM, MINIMAL_LENGTH_ORDER-1 );
		final float dHeightCM = (float)SGUtilityNumber.roundOffNumber( heightCM, MINIMAL_LENGTH_ORDER-1 );

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

		// set the bounds of paper
		this.mPaperSize.setValues( width, height );

		return true;
	}


	private boolean setPaperSizeRoundingOutInCMUnit(
		final float widthCM, final float heightCM )
	{
		// round out the size
		final float dWidthCM = (float)SGUtilityNumber.roundOutNumber( widthCM, MINIMAL_LENGTH_ORDER-1 );
		final float dHeightCM = (float)SGUtilityNumber.roundOutNumber( heightCM, MINIMAL_LENGTH_ORDER-1 );

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

		// set the bounds of paper
		this.mPaperSize.setValues( width, height );

		return true;
	}


	private boolean setPaperSizeRoundingOff(
		final float widthPt, final float heightPt )
	{
		final float ratio = SGIConstants.CM_POINT_RATIO;
		final float widthCM = widthPt*ratio;
		final float heightCM = heightPt*ratio;

		this.setPaperSizeRoundingOffInCMUnit( widthCM, heightCM );

		return true;
	}


	private boolean setPaperSizeRoundingOut(
		final float widthPt, final float heightPt )
	{
		final float ratio = SGIConstants.CM_POINT_RATIO;
		final float widthCM = widthPt*ratio;
		final float heightCM = heightPt*ratio;

		this.setPaperSizeRoundingOutInCMUnit( widthCM, heightCM );

		return true;
	}


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



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

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


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

		this.mTemporaryProperties = null;

		this.getContentPane().repaint();

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

		return true;
	}


	
	/**
	 * 
	 */
	private boolean mChangedFlag = false;


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

		this.updateUndoItems();
		
		return true;
	}


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


	/**
	 * 
	 */
	protected boolean setDefaultCursor()
	{
		Cursor cur = Cursor.getDefaultCursor();
		this.setCursor( cur );


		return true;
	}



	/**
	 * 
	 * @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
	 */
	private boolean updateClientRect()
	{
		this.updateClientRectOld();
//		this.updateClientRectNew();
		return true;
	}



	/**
	 * 
	 * @return
	 */
	private boolean updateClientRectNew()
	{
System.out.println("-- updateClientRect --");

		Rectangle2D cRect = this.getClientRect();
		Rectangle2D pRect = this.getPaperRect();
		final float margin = PAPER_MARGIN;
		Rectangle2D bbRect = this.getBoundingBox();
		Rectangle2D vpRect = this.getViewportBounds();
		ArrayList fList = this.getVisibleFigureList();
//		Rectangle2D fRect
//			= this.getBoundingBoxOfFigures( fList );

		final float mag = this.getMagnification();

		final float cx = (float)cRect.getX();
		final float cy = (float)cRect.getY();
		final float cw = (float)cRect.getWidth();
		final float ch = (float)cRect.getHeight();

		final float vx = (float)vpRect.getX();
		final float vy = (float)vpRect.getY();
		final float vw = (float)vpRect.getWidth();
		final float vh = (float)vpRect.getHeight();

		final float px = this.getPaperX();
		final float py = this.getPaperY();
		final float pw = mag*this.getPaperWidth();
		final float ph = mag*this.getPaperHeight();


//System.out.println("cRect:"+cRect);
//System.out.println("pRect:"+pRect);
//System.out.println("bbRect:"+bbRect);
//System.out.println("vpRect:"+vpRect);


//		final boolean movedLeft
//			= ( (int)cRect.getX()!=(int)bbRect.getX() );
//		final boolean movedTop
//			= ( (int)cRect.getY()!=(int)bbRect.getY() );
//		final boolean movedRight
//			= ( (int)(cRect.getX()+cRect.getWidth())!=(int)(bbRect.getX()+bbRect.getWidth()) );
//		final boolean movedBottom
//			= ( (int)(cRect.getY()+cRect.getHeight())!=(int)(bbRect.getY()+bbRect.getHeight() ) );
//
//System.out.println("left:"+movedLeft);
//System.out.println("right:"+movedRight);
//System.out.println("top:"+movedTop);
//System.out.println("bottom:"+movedBottom);


		Rectangle2D cRectNew = new Rectangle2D.Float();
		cRectNew.setRect( bbRect );
		{
			final boolean minXFlag = ( (int)vx <= (int)cRectNew.getX() );
			final boolean maxXFlag = ( (int)( cRectNew.getX() + cRectNew.getWidth() ) <= (int)( vx + vw ) );
			double xNew = cRectNew.getX();
			double wNew = cRectNew.getWidth();
			if( minXFlag & maxXFlag )
			{
				xNew = vx;
				wNew = vw;
			}
			else if( minXFlag )
			{
				xNew = vx;
				if( wNew < vw )
				{
					wNew = vw;
				}
			}
			else if( maxXFlag )
			{
				xNew = vx + vw - wNew;
				if( vx < xNew )
				{
					xNew = vx;
				}
				if( wNew < vw )
				{
					wNew = vw;
				}
			}
			cRectNew.setRect( xNew, cRectNew.getY(), wNew, cRectNew.getHeight() );
		}

		{
			final boolean minYFlag = ( vy < cRectNew.getMinY() );
			final boolean maxYFlag = ( cRectNew.getMinY() + cRectNew.getHeight() < vy + vh );
			double yNew = cRectNew.getY();
			double hNew = cRectNew.getHeight();
			if( minYFlag & maxYFlag )
			{
				yNew = vy;
				hNew = vh;
			}
			else if( minYFlag )
			{
				yNew = vy;
				if( hNew < vh )
				{
					hNew = vh;
				}
			}
			else if( maxYFlag )
			{
				yNew = vy + vh - hNew;
				if( vy < yNew )
				{
					yNew = vy;
				}
				if( hNew < vh )
				{
					hNew = vh;
				}
			}
			cRectNew.setRect( cRectNew.getX(), yNew, cRectNew.getWidth(), hNew );			
		}

this.mTempRect.setRect( cRectNew );


		// set paper origin
		float pXNew;
		float pYNew;
		if( fList.size()==0 )
		{
			pXNew = (float)cRectNew.getX() + 0*margin;
			pYNew = (float)cRectNew.getY() + 0*margin;
		}
		else
		{
			SGFigure left = null;
			SGFigure top = null;
			float minX = px;
			float minY = py;
			for( int ii=0; ii<fList.size(); ii++ )
			{
				SGFigure f = (SGFigure)fList.get(ii);
				Rectangle2D bb = f.getBoundingBox();
				final float x = (float)( bb.getX() );
				final float y = (float)( bb.getY() );
				if( x < minX )
				{
					minX = x;
					left = f;
				}
				if( y < minY )
				{
					minY = y;
					top = f;
				}
			}
			if( left!=null )
			{
				pXNew = px - minX;
			}
			else
			{
				pXNew = px;
			}
			if( top!=null )
			{
				pYNew = py - minY;
			}
			else
			{
				pYNew = py;
			}
		}

System.out.println(px+"  "+py);
System.out.println(pXNew+"  "+pYNew);

		// set client rectangle
		this.setClientRect( cRectNew );
		this.setPaperOrigin( pXNew, pYNew );
		this.updateGraphRectOfAllFigures();

		//
		this.setEnableScrollBars();

		//
		this.setScrollBarValue();


System.out.println();


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


		//
		this.setScrollBarValue();


		//
		this.setEnableScrollBars();


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


		//
		this.setScrollBarValue();

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




	/**
	 * スクロールバーの表示／非表示切り替え
	 * @return
	 */
	protected boolean setEnableScrollBars()
	{
		Rectangle vpRect = this.getViewportBounds().getBounds();
		Rectangle bbRect = this.getClientRect().getBounds();

		final boolean hFlag
			= SGUtilityNumber.contains(
				vpRect.x, vpRect.x + vpRect.width,
				bbRect.x, bbRect.x + bbRect.width );

		final boolean vFlag
			= SGUtilityNumber.contains(
				vpRect.y, vpRect.y + vpRect.height,
				bbRect.y, bbRect.y + bbRect.height );

		this.mHScrollBar.setVisible(!hFlag);
		this.mVScrollBar.setVisible(!vFlag);

//System.out.println(vpRect);
//System.out.println(bbRect);
//System.out.println(hFlag+"  "+vFlag);

		return true;
	}



	/**
	 * 
	 */
	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.mPaperSize.setValues( w.floatValue(), h.floatValue() );

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

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

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

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

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

		this.setVisibleFigure( wp.getVisibleFigureList() );

		return true;
	}



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

		final WindowProperties p = new WindowProperties();

		p.setPaperWidth( this.mPaperSize.x );
		p.setPaperHeight( this.mPaperSize.y );
		p.setBackGroundColor( this.getPaperColor() );
		p.setGridColor( this.getGridLineColor() );
		p.setGridVisible( this.isGridLineVisible() );
		p.setGridInterval( this.getGridLineInterval() );
		p.setGridLineWidth( this.getGridLineWidth() );
		p.setVisibleFigureList( this.getVisibleFigureList() );

		return p;

	}


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



	/**
	 * 
	 * @return
	 */
	public Rectangle2D getClientRect()
	{
		if( this.mClientRect==null )
		{
			return null;
		}
		else
		{
			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.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 = RulerPanel.RULER_WIDTH;
		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.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
	 */
	public 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();

		return true;
	}


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

		// update
		this.updateOnUndo();


		return true;
	}



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


	/**
	 * 
	 */
	public void setChanged( final boolean b )
	{
		this.mChangedFlag = 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
	{
		SGINode node;
		NodeMenuItem( String text )
		{
			super( text );
		}

		SGINode getNode()
		{
			return this.node;
		}
		
		void setNode( SGINode node )
		{
			this.node = node;
		}
	}




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


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


	private boolean toImage( final int mode )
	{
		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 );

		SGIImageExportManager man = this.mImageExportManager;

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

				// Create a panel object and add the target object to it,
				// and replaced the target object with it.
				JPanel panel = new JPanel();
				panel.setLayout(null);
				panel.setSize( width, height );
				panel.add(target);
				panel.setOpaque(true);
				panel.setBackground( target.getBackground() );
				
				ret = man.print( panel, this, width, height );
				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;
		SGTuple2f[] locationArray;
		ArrayList visibleList;
	}




	/**
	 * 
	 * @param ePanel
	 * @param info
	 * @return
	 */
	private boolean beforeExport(
		final ExportPanel ePanel, final InfoForExport info )
	{
		float mag = this.mMagnification;
		float hValue = this.getHScrollValue();
		float vValue = this.getVScrollValue();
		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 int width = (int)this.mPaperSize.x;
		final int height = (int)this.mPaperSize.y;

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

		// add figures to the export panel
		Rectangle2D vBounds = new Rectangle2D.Float(
			0.0f, 0.0f, this.getPaperWidth(), this.getPaperHeight() );
		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 invisible
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGFigure figure = (SGFigure)list.get(ii);
			figure.setVisible(false);
		}


		// 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.setScrollValue( this.mHScrollBar, hValue );
		this.setScrollValue( this.mVScrollBar, vValue );

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

		return true;
	}



	/**
	 * A panel class used to export images.
	 *
	 */
	public static class ExportPanel extends JPanel
	{
		/**
		 * The list of printable objects.
		 */
		private ArrayList mPrintableList = new ArrayList();
		
		/**
		 * 
		 */
		private boolean mClipFlag = true;

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

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

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

	}



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





	/**
	 * 
	 * @param document
	 * @return
	 */
	public Element createElement( final Document document )
	{
		Element element = document.createElement( SGDrawingWindow.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( SGDrawingWindow.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 )	+ SGUtilityNumber.cm );
		element.setAttribute(
			KEY_PAPER_HEIGHT,
			Float.toString( height ) + SGUtilityNumber.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.mPaperSize.x*SGIConstants.CM_POINT_RATIO )
				+ SGUtilityNumber.cm );
		element.setAttribute(
			KEY_PAPER_HEIGHT,
			Float.toString( this.mPaperSize.y*SGIConstants.CM_POINT_RATIO )
			+ SGUtilityNumber.cm );

		this.writeProperty_(element);

		return true;
	}

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

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

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

	}




//
// 内部クラス
//


	/**
	 * Base class of panel class in the window.
	 *
	 */
	private static abstract class InnerPanel extends JPanel
	{
		/**
		 * 
		 */
		protected float mMagnification = 1.0f;


		/**
		 * 
		 */
		protected SGDrawingWindow mWnd = null;


		/**
		 * 
		 * @param wnd
		 */		
		protected InnerPanel( SGDrawingWindow wnd )
		{
			super();
			this.mWnd = wnd;
		}


		/**
		 * @param f
		 */
		public void setMagnification( final float f )
		{
			this.mMagnification = f;
		}
		
	}



	/**
	 * Panel to draw anchors and rubber band.
	 */
	private static class ForegroundPanel extends InnerPanel
	{
		/**
		 * 
		 */
		public static final float ANCHOR_SIZE = 6.0f;

		/**
		 * 
		 */
		protected ForegroundPanel( SGDrawingWindow wnd )
		{
			super( wnd );
			this.setVisible(true);
			this.setOpaque(false);
		}


		/**
		 * 
		 */
		public void paintComponent( Graphics g )
		{
			super.paintComponent(g);

			final Graphics2D g2d = (Graphics2D)g.create();

			ArrayList list = this.mWnd.getFocusedObjectsList();
			for( int ii=0; ii<list.size(); ii++ )
			{

				final SGFigure figure = (SGFigure)list.get(ii);
				if( figure.isSelectionSymbolsVisible() == false )
				{
					continue;
				}

				// ドラッグ中の矩形
				Rectangle2D dRect = figure.getRubberBandRect();
				if( SGFigure.mRubberBandFlag & SGFigure.mRubberBandVisibleFlag )
				{
					this.drawRubberBand( g2d, dRect.getBounds() );
				}


				// アンカー
				{
					final Rectangle2D gRect = figure.getGraphRect();
					final int x = (int)gRect.getX();

					final int y = (int)gRect.getY();
					final int w = (int)gRect.getWidth();
					final int h = (int)gRect.getHeight();
					drawAnchor( g2d, x, y );
					drawAnchor( g2d, x+w, y );
					drawAnchor( g2d, x, y+h );
					drawAnchor( g2d, x+w, y+h );
					drawAnchor( g2d, x+w/2, y );
					drawAnchor( g2d, x, y+h/2 );
					drawAnchor( g2d, x+w/2, y+h );
					drawAnchor( g2d, x+w, y+h/2 );
				}


			}


		}



		/**
		 * 
		 */
		private void drawAnchor( final Graphics2D g2d, final int x, final int y )
		{
			final float size = ANCHOR_SIZE;
			final Shape anchor = new Ellipse2D.Float(
				x-0.5f*size, y-0.5f*size, size, size
			);

			g2d.setPaint(Color.BLACK);
			g2d.setStroke( new BasicStroke(3) );
			g2d.draw(anchor);

			g2d.setPaint(Color.WHITE);
			g2d.fill(anchor);
		}



		/**
		 * 
		 */
		private void drawRubberBand( final Graphics2D g2d, final Rectangle rect )
		{

			g2d.setPaint(Color.BLACK);

			final float width = 2.0f;
			final float dash[] = {2.0f*width,width};
			g2d.setStroke
			(
				new BasicStroke
				(
					width,
					BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
					10.0f, dash, 0.0f
				)
			);

			g2d.draw( rect );

//			final int x = rect.x;
//			final int y = rect.y;
//			final int w = rect.width;
//			final int h = rect.height;
//			final Line2D north = new Line2D.Float( x, y, x+w, y );
//			final Line2D south = new Line2D.Float( x, y+h, x+w, y+h );
//			final Line2D west = new Line2D.Float( x, y, x, y+h );
//			final Line2D east = new Line2D.Float( x+w, y, x+w, y+h );
//
//			g2d.draw( north );
//			g2d.draw( south );
//			g2d.draw( west );
//			g2d.draw( east );

		}

		
	}




	private static class FigurePanel extends InnerPanel
	{
		/**
		 * 
		 */
		protected FigurePanel( SGDrawingWindow wnd )
		{
			super( wnd );
			this.setVisible(true);
			this.setOpaque(false);
		}


		public void paintComponent( Graphics g )
		{
			super.paintComponent(g);
//System.out.println("paintComponent");
			SGFigure[] array = this.mWnd.getFigureArray();
			for( int ii=0; ii<array.length; ii++ )
			{
				if( array[ii].isVisible() )
				{
					array[ii].paint( g, true );
				}
			}

		}


	}



	/**
	 * Panel to draw grid lines and paper.
	 */
	private static class BackgroundPanel extends InnerPanel
	{
		/**
		 * 
		 */
		private boolean mGridVisibleFlag = SGDefaultValues.GRID_VISIBLE;


		/**
		 * 
		 */
		private float mGridInterval = SGDefaultValues.GRID_INTERVAL;


		/**
		 * 
		 */
		private float mGridLineWidth = SGDefaultValues.GRID_LINE_WIDTH;


		/**
		 * 
		 */
		private Color mGridLineColor = SGDefaultValues.GRID_LINE_COLOR;


		/**
		 * 
		 */
		private Color mPaperColor = SGDefaultValues.WINDOW_PAPER_COLOR;


		/**
		 * 
		 */
		protected BackgroundPanel( SGDrawingWindow wnd )
		{
			super( wnd );
			this.setVisible(true);
			this.setOpaque(true);
		}


		/**
		 * 
		 */
		public void paintComponent( final Graphics g )
		{
			super.paintComponent(g);
			final Graphics2D g2d = (Graphics2D)g;


			// get the rectangle of paper
			Rectangle2D pRect = this.mWnd.getPaperRect();
			final float px = (float)pRect.getX();
			final float py = (float)pRect.getY();
			final float pw = (float)pRect.getWidth();
			final float ph = (float)pRect.getHeight();

			Rectangle2D vpRect = this.mWnd.getViewportBounds();
			final float vx = (float)vpRect.getX();
			final float vy = (float)vpRect.getY();
			final float vw = (float)vpRect.getWidth();
			final float vh = (float)vpRect.getHeight();


			// fill the paper rectangle
			g2d.setPaint( this.getPaperColor() );
			g2d.fill( pRect );

			final Line2D line = new Line2D.Float();


			// draw the grid lines
			if( this.isGridVisible() )
			{

				// set the property of grid lines
				g2d.setStroke( new BasicStroke( this.getGridLineWidth() ) );
				g2d.setPaint( this.getGridLineColor() );


				// vertical lines
				final float[] xArray = this.getVerticalGridLocation();
				for( int ii=0; ii<xArray.length; ii++ )
				{
					final float x = xArray[ii];
					line.setLine( x, py, x, py + ph );
					g2d.draw( line );
				}


				// horizontal lines
				final float[] yArray = this.getHorizontalGridLocation();
				for( int ii=0; ii<yArray.length; ii++ )
				{
					final float y = yArray[ii];
					line.setLine( px, y, px + pw, y );
					g2d.draw( line );
				}

			}


			// draw the edge of paper
			g2d.setPaint( Color.BLACK );
			g2d.setStroke( new BasicStroke(1) );
			g2d.draw( pRect );



//			final float eWidth = 5.0f;
//
//			g2d.setStroke( new BasicStroke( eWidth ) );
//
//			final double x = pRect.getX() + pRect.getWidth() + eWidth/2.0;
//			line.setLine(
//				x, pRect.getY() + eWidth,
//				x, pRect.getY() + pRect.getHeight() + eWidth/2.0
//			);
//			g2d.draw( line );
//
//			final double y = pRect.getY() + pRect.getHeight() + eWidth/2.0;
//			line.setLine(
//				pRect.getX() + eWidth, y,
//				pRect.getX() + pRect.getWidth() + eWidth/2.0, y
//			);
//			g2d.draw( line );


//g2d.setPaint( Color.BLUE );
//g2d.setStroke( new BasicStroke( 8.0f ) );
//g2d.draw( this.mWnd.getClientRect() );
////g2d.draw( this.mWnd.mTempRect );
//
//g2d.setPaint( Color.RED );
//float[] dash = { 5.0f, 5.0f };
//g2d.setStroke( new BasicStroke( 5.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f, dash, 0.0f ) );
//g2d.draw( this.mWnd.getBoundingBox() );

		}


		/**
		 * 
		 * @return
		 */
		public float[] getVerticalGridLocation()
		{
			final float space = this.mMagnification*this.getGridInterval();

			// get the rectangle of paper
			Rectangle2D pRect = this.mWnd.getPaperRect();
			final float px = (float)pRect.getX();
			final float pw = (float)pRect.getWidth();

			Rectangle2D vpRect = this.mWnd.getViewportBounds();
			final float vx = (float)vpRect.getX();
			final float vw = (float)vpRect.getWidth();

			ArrayList list = new ArrayList();
			int cnt = (int)( ( vx - px )/space ) + 1;
			while( true )
			{
				final float x = cnt*space + px;

				if( x > vx + vw )
				{
					break;
				}

				if( x > px + pw )
				{
					break;
				}

				list.add( new Float(x) );
				cnt++;
			}

			final float[] array = new float[list.size()];
			for( int ii=0; ii<array.length; ii++ )
			{
				array[ii] = ((Float)list.get(ii)).floatValue();
			}

			return array;
		}


		/**
		 * 
		 * @return
		 */
		public float[] getHorizontalGridLocation()
		{
			final float space = this.mMagnification*this.getGridInterval();

			// get the rectangle of paper
			Rectangle2D pRect = this.mWnd.getPaperRect();
			final float py = (float)pRect.getY();
			final float ph = (float)pRect.getHeight();

			Rectangle2D vpRect = this.mWnd.getViewportBounds();
			final float vy = (float)vpRect.getY();
			final float vh = (float)vpRect.getHeight();

			ArrayList list = new ArrayList();
			int cnt = (int)( ( vy - py )/space ) + 1;
			while( true )
			{
				final float y = cnt*space + py;

				if( y > vy + vh )
				{
					break;
				}

				if( y > py + ph )
				{
					break;
				}

				list.add( new Float(y) );
				cnt++;
			}

			final float[] array = new float[list.size()];
			for( int ii=0; ii<array.length; ii++ )
			{
				array[ii] = ((Float)list.get(ii)).floatValue();
			}

			return array;
		}



		/**
		 * @return
		 */
		public float getGridInterval()
		{
			return mGridInterval;
		}

		/**
		 * @return
		 */
		public Color getGridLineColor()
		{
			return mGridLineColor;
		}

		/**
		 * @return
		 */
		public float getGridLineWidth()
		{
			return mGridLineWidth;
		}

		/**
		 * @return
		 */
		public boolean isGridVisible()
		{
			return mGridVisibleFlag;
		}

		/**
		 * @param f
		 */
		public void setGridInterval( final float f )
		{
			mGridInterval = f;
		}

		/**
		 * @param color
		 */
		public void setGridLineColor( final Color color )
		{
			mGridLineColor = color;
		}

		/**
		 * @param f
		 */
		public void setGridLineWidth( final float f )
		{
			mGridLineWidth = f;
		}

		/**
		 * @param b
		 */
		public void setGridVisible( final boolean b )
		{
			mGridVisibleFlag = b;
		}

		/**
		 * @return
		 */
		public Color getPaperColor()
		{
			return mPaperColor;
		}

		/**
		 * @param color
		 */
		public void setPaperColor(Color color)
		{
			mPaperColor = color;
		}

	}




	/**
	 * Panel to draw rulers.
	 */
	private static class RulerPanel extends InnerPanel
	{
		/**
		 * 
		 */
		private static final int RULER_WIDTH = 20;
		private static final int ANCHOR_SIZE = 12;
		private static final Color INNER_COLOR_1 = new Color(234,238,232);
		private static final Color INNER_COLOR_2 = new Color(124,155,64);

		private static final Color ANCHOR_INNER_COLOR = new Color(222,222,222);
		private static final float ANCHOR_EDGE_LINE_WIDTH = 4.0f;
		private static final Color ANCHOR_EDGE_LINE_COLOR = Color.WHITE;
		private static final Color LINE_COLOR = Color.BLACK;
		private static final float LINE_WIDTH = 2.0f;
		private static final BasicStroke LINE_STROKE
			= new BasicStroke(
				LINE_WIDTH,
				BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
				1.0f,
				new float[]{ 5*LINE_WIDTH, LINE_WIDTH, LINE_WIDTH, 2*LINE_WIDTH },
				0.0f
			);



		/**
		 * 
		 */
		private Font mRulerFont = null;
		private Point2D mHorizontalLocation = null;
		private Point2D mVerticalLocation = null;
		private boolean mDrawHorizontalLineFlag = false;
		private boolean mDrawVerticalLineFlag = false;



		/**
		 * 
		 */
		RulerPanel( SGDrawingWindow wnd )
		{
			super( wnd );
			
			this.setVisible(true);
			this.setOpaque(false);

			final int style = SGUtilityText.getFontStyle(
				SGDefaultValues.RULER_FONT_STYLE );

			this.mRulerFont = new Font(
				SGDefaultValues.RULER_FONT_NAME,
				style,
				(int)SGDefaultValues.RULER_FONT_SIZE
			);
		}


		/**
		 * 
		 */
		public void paintComponent( Graphics g )
		{
			super.paintComponent(g);
			final Graphics2D g2d = (Graphics2D)g;

//			if( mRulerVisibleFlag )
			{
				this.drawRuler(g2d);
			}


			// lines
			if( this.mDrawHorizontalLineFlag )
			{
				this.drawHorizontalLine(g2d);
			}

			if( this.mDrawVerticalLineFlag )
			{
				this.drawVerticalLine(g2d);
			}


			//
			// anchor
			//


			// horizontal
			{
				Point2D pos = null;
				if( this.mDrawHorizontalLineFlag )
				{
					pos = this.mHorizontalLocation;
				}
				else
				{
					pos = this.getHorizontalAnchorLocationFromPaper();
				}
				this.drawHorizontalAnchor((int)pos.getX(),(Graphics2D)g2d.create());
			}

			// vertical
			{
				Point2D pos = null;
				if( this.mDrawVerticalLineFlag )
				{
					pos = this.mVerticalLocation;
				}
				else
				{
					pos = this.getVerticalAnchorLocationFromPaper();
				}
				this.drawVerticalAnchor((int)pos.getY(),(Graphics2D)g2d.create());
			}

		}



		private void drawHorizontalAnchor(
			final int x, final Graphics2D g2d )
		{
			
			final int nPoints = 7;
			final int[] xPos = new int[nPoints];
			final int[] yPos = new int[nPoints];

			final int y = (int)this.getHorizontalAnchorLocationFromPaper().getY() -1;

			xPos[0] = x + 3;
			yPos[0] = y - 10;
			
			xPos[1] = xPos[0];
			yPos[1] = y - 6;

			xPos[2] = x + 6;
			yPos[2] = yPos[1];

			xPos[3] = x;
			yPos[3] = y;
			
			xPos[4] = x - 6;
			yPos[4] = yPos[2];

			xPos[5] = x - 3;
			yPos[5] = yPos[4];
			
			xPos[6] = xPos[5];
			yPos[6] = yPos[0];


			this.drawAnchor( xPos, yPos, nPoints, g2d );
		}


		private void drawVerticalAnchor(
			final int y, final Graphics2D g2d )
		{
			final int size = ANCHOR_SIZE;
			final int nPoints = 7;
			final int[] xPos = new int[nPoints];
			final int[] yPos = new int[nPoints];

			final int x = (int)this.getVerticalAnchorLocationFromPaper().getX() -1;

			xPos[0] = x - 10;
			yPos[0] = y + 3;
			
			xPos[1] = x - 6;
			yPos[1] = yPos[0];

			xPos[2] = xPos[1];
			yPos[2] = y + 6;

			xPos[3] = x;
			yPos[3] = y;
			
			xPos[4] = xPos[2];
			yPos[4] = y - 6;

			xPos[5] = xPos[4];
			yPos[5] = y - 3;
			
			xPos[6] = xPos[0];
			yPos[6] = yPos[5];


			this.drawAnchor( xPos, yPos, nPoints, g2d );
		}



		private void drawAnchor(
			final int[] xPos, final int[] yPos, final int nPoints,
			final Graphics2D g2d )
		{
			final Shape sh
				= new Polygon( xPos, yPos, nPoints );

			final int x1 = xPos[6];
			final int y1 = yPos[6];
			final int x2 = ( xPos[2] + xPos[3] )/2;
			final int y2 = ( yPos[2] + yPos[3] )/2;

			GradientPaint gPaint = new GradientPaint(
				x1, y1, INNER_COLOR_1, x2, y2, INNER_COLOR_2 );
			g2d.setPaint( gPaint );
			g2d.fill(sh);

			g2d.setStroke( new BasicStroke(1) );
			g2d.setRenderingHint(
				RenderingHints.KEY_ANTIALIASING, 
				RenderingHints.VALUE_ANTIALIAS_ON
			);
			g2d.setPaint( Color.BLACK );
			g2d.draw(sh);
			
		}


		private void drawHorizontalLine( final Graphics2D g2d )
		{
			if( this.mHorizontalLocation==null )
			{
				return;
			}
			Point2D pos = this.mHorizontalLocation;
			final float x = (float)pos.getX();
			Line2D line = new Line2D.Float();
			line.setLine(
				pos.getX(),
				this.getHorizontalAnchorLocationFromPaper().getY(),
				pos.getX(),
				this.getHeight()
			);
			g2d.setPaint( LINE_COLOR );
			g2d.setStroke( LINE_STROKE );
			g2d.draw(line);
		}


		private void drawVerticalLine( final Graphics2D g2d )
		{
			if( this.mVerticalLocation==null )
			{
				return;
			}
			Point2D pos = this.mVerticalLocation;
			Line2D line = new Line2D.Float();
			line.setLine(
				this.getVerticalAnchorLocationFromPaper().getX(),
				pos.getY(),
				this.getWidth(),
				pos.getY()
			);
			g2d.setPaint( LINE_COLOR );
			g2d.setStroke( LINE_STROKE );
			g2d.draw(line);
		}


		/**
		 * 
		 * @return
		 */
		private boolean isPressed()
		{
			return this.mDrawHorizontalLineFlag | this.mDrawVerticalLineFlag;
		}


		/**
		 * 
		 * @param e
		 * @return
		 */
		private boolean pressed( MouseEvent e )
		{
			final int size = 10;

			{
				Point2D pos = this.getHorizontalAnchorLocationFromPaper();
				final int posX = (int)pos.getX();
				final int posY = (int)pos.getY();
				Rectangle rect = new Rectangle(
					posX - size/2, posY - size,
					size, size
				);

				if( rect.contains( e.getPoint() ) )
				{
					this.mDrawHorizontalLineFlag = true;
					this.mHorizontalLocation = pos;
					this.repaint();
					return true;
				}
			}

			{
				Point2D pos = this.getVerticalAnchorLocationFromPaper();
				final int posX = (int)pos.getX();
				final int posY = (int)pos.getY();
				Rectangle rect = new Rectangle(
					posX - size, posY - size/2,
					size, size
				);

				if( rect.contains( e.getPoint() ) )
				{
					this.mDrawVerticalLineFlag = true;
					this.mVerticalLocation = pos;
					this.repaint();
					return true;
				}
			}

			this.mDrawHorizontalLineFlag = false;
			this.mDrawVerticalLineFlag = false;
			this.mHorizontalLocation = null;
			this.mVerticalLocation = null;
			this.repaint();

			return false;
		}


		private boolean dragged( MouseEvent e )
		{
			this.mHorizontalLocation = e.getPoint();
			this.mVerticalLocation = e.getPoint();
			return true;
		}


		private boolean released( MouseEvent e )
		{
			final int x = e.getX();
			final int y = e.getY();

			Rectangle2D paper = this.mWnd.getPaperRect();
			final float startX = this.getPaperStartX();
			final float startY = this.getPaperStartY();

			final float wOld = (float)paper.getWidth();
			final float hOld = (float)paper.getHeight();
			
			final float ratio = SGIConstants.CM_POINT_RATIO;
			final float minWidth = SGDrawingWindow.PAPER_WIDTH_MIN_VALUE/ratio;
			final float minHeight = SGDrawingWindow.PAPER_HEIGHT_MIN_VALUE/ratio;
			final float maxWidth = SGDrawingWindow.PAPER_WIDTH_MAX_VALUE/ratio;
			final float maxHeight = SGDrawingWindow.PAPER_HEIGHT_MAX_VALUE/ratio;

			if( this.mDrawHorizontalLineFlag )
			{
				float w = ( x - startX )/this.mMagnification;
				if( w < minWidth )
				{
					w = minWidth;
				}
				if( w > maxWidth )
				{
					w = maxWidth;
				}

				this.mWnd.setPaperSizeRoundingOff( w, this.mWnd.getPaperHeight() );
				this.mWnd.setChanged(true);
			}
			
			if( this.mDrawVerticalLineFlag )
			{
				float h = ( y - startY )/this.mMagnification;
				if( h < minHeight )
				{
					h = minHeight;
				}
				if( h > maxHeight )
				{
					h = maxHeight;
				}

				this.mWnd.setPaperSizeRoundingOff( this.mWnd.getPaperWidth(), h );
				this.mWnd.setChanged(true);
			}

			if( this.mDrawHorizontalLineFlag | this.mDrawVerticalLineFlag )
			{
				this.mWnd.updateClientRect();
				this.mWnd.notifyToRoot();
				this.repaint();
			}

			this.mDrawHorizontalLineFlag = false;
			this.mDrawVerticalLineFlag = false;
			this.mHorizontalLocation = null;
			this.mVerticalLocation = null;

			return true;
		}


		private Point2D getHorizontalAnchorLocationFromPaper()
		{
			Rectangle2D rect = this.mWnd.getPaperRect();
			final float x = (float)rect.getX();
			final float y = (float)rect.getY();
			final float w = (float)rect.getWidth();
			final int rw = RulerPanel.RULER_WIDTH;
			Point2D pos = new Point2D.Float( x + rw + w, rw );
			return pos;
		}


		private Point2D getVerticalAnchorLocationFromPaper()
		{
			Rectangle2D rect = this.mWnd.getPaperRect();
			final float x = (float)rect.getX();
			final float y = (float)rect.getY();
			final float h = (float)rect.getHeight();
			final int rw = RulerPanel.RULER_WIDTH;
			Point2D pos = new Point2D.Float( rw, y + rw + h );
			return pos;
		}


		private float getPaperStartX()
		{
			Rectangle2D rect = this.mWnd.getPaperRect();
			final float x = (float)rect.getX();
			final int rw = RulerPanel.RULER_WIDTH;
			return x + rw;			
		}


		private float getPaperStartY()
		{
			Rectangle2D rect = this.mWnd.getPaperRect();
			final float y = (float)rect.getY();
			final int rw = RulerPanel.RULER_WIDTH;
			return y + rw;
		}


		private float getPaperEndX()
		{
			Rectangle2D rect = this.mWnd.getPaperRect();
			final float x = (float)rect.getX();
			final float w = (float)rect.getWidth();
			final int rw = RulerPanel.RULER_WIDTH;
			return x + rw + w;
		}


		private float getPaperEndY()
		{
			Rectangle2D rect = this.mWnd.getPaperRect();
			final float y = (float)rect.getY();
			final float h = (float)rect.getHeight();
			final int rw = RulerPanel.RULER_WIDTH;
			return y + rw + h;
		}



		/**
		 * 
		 */
		private boolean drawRuler( final Graphics2D g2d )
		{

			//
			final int rw = RULER_WIDTH;
			final int width = this.getWidth();
			final int height = this.getHeight();

			final int nPoints = 6;
			final int[] xPoints = new int[nPoints];
			final int[] yPoints = new int[nPoints];

			xPoints[0] = 0;
			yPoints[0] = 0;
			
			xPoints[1] = width;
			yPoints[1] = 0;
			
			xPoints[2] = width;
			yPoints[2] = rw;
			
			xPoints[3] = rw;
			yPoints[3] = rw;
			
			xPoints[4] = rw;
			yPoints[4] = height;
			
			xPoints[5] = 0;
			yPoints[5] = height;

			final Polygon polygon
				= new Polygon( xPoints, yPoints, nPoints );


			//
			// paint
			//

			Color bgColor = this.getBackground();
			Color lineColor = Color.BLACK;

			g2d.setPaint( bgColor );
			g2d.fill( polygon );
			g2d.setPaint( lineColor );
			g2d.setStroke( new BasicStroke(1) );
			g2d.draw( polygon );


			// draw the lines of rulers
			this.drawNumbersAndLines( g2d );


			// corner
			final Rectangle2D rectCorner
				= new Rectangle2D.Float( 0.0f, 0.0f, rw, rw );
			g2d.setPaint( bgColor );
			g2d.fill( rectCorner );
			g2d.setPaint( lineColor );
			g2d.draw( rectCorner );


//			g2d.setStroke( new BasicStroke(2) );
//
//			Line2D left = new Line2D.Float( 0.0f, 0.0f, 0.0f, rw );
//			Line2D top = new Line2D.Float( 0.0f, 0.0f, rw, 0.0f );
//			g2d.setPaint( Color.WHITE );
//			g2d.draw( left );
//			g2d.draw( top );
//
//			Line2D right = new Line2D.Float( rw, 0.0f, rw, rw );
//			Line2D bottom = new Line2D.Float( 0.0f, rw, rw, rw );
//			g2d.setPaint( Color.BLACK );
//			g2d.draw( right );
//			g2d.draw( bottom );

			return true;

		}




		/**
		 * 
		 */
		private boolean drawNumbersAndLines( final Graphics2D g2d )
		{

			final int rw = RULER_WIDTH;
			final float mag = this.mMagnification;
			final int width = this.getWidth();
			final int height = this.getHeight();
			final Rectangle vpRect
				= new Rectangle( 0, 0, width - rw, height - rw );

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

//			final float hStart = rw + (float)cRect.getX();
//			final float vStart = rw + (float)cRect.getY();

			final Line2D line = new Line2D.Float();

			g2d.setPaint( Color.BLACK );


			//
			// axis lines
			//

			g2d.setStroke( new BasicStroke(2) );

			// horizontal
			{
				final float x1 = rw;
				final float x2 = width;
				final float y = rw;
				line.setLine( x1, y, x2, y );
				g2d.draw(line);
			}

			// perpendicular
			{
				final float x = rw;
				final float y1 = rw;
				final float y2 = height;
				line.setLine( x, y1, x, y2 );
				g2d.draw(line);			
			}



			//
			// scale lines and numbers
			//

			Font font = this.mRulerFont;
			g2d.setStroke( new BasicStroke(1) );
			g2d.setFont( font );
			final float fSize = font.getSize();


			int offset;
			int subNum;
			if( mag < 0.50f )
			{
				offset = 4;
				subNum = 8;
			}
			else if( mag < 1.0f )
			{
				offset = 2;
				subNum = 4;
			}
			else
			{
				offset = 1;
				subNum = 4;
			}


			final float factor = mag/CM_POINT_RATIO;

			// horizontal
			{
				final ArrayList numberList = new ArrayList();
				final float px = (float)pRect.getX();
				final float pOffset = - px;
				final float pOffsetInRulerUnit = pOffset/factor;
				final float endLocation = (float)vpRect.getWidth() + pOffset;
				int diff = (int)pOffsetInRulerUnit;
				if( diff%offset!=0 )
				{
					diff = (diff/offset)*offset;
				}

				int cnt = diff;
				while( true )
				{
//System.out.print(cnt+" ");
					final float location = factor*cnt;
					if( location > endLocation )
					{
						break;
					}

					numberList.add( new Integer(cnt) );
					cnt+=offset;
				}
//System.out.println();


				// draw
				for( int ii=0; ii<numberList.size(); ii++ )
				{
					// main
					final int num = ((Integer)numberList.get(ii)).intValue();
					final float location = num*factor;
					final float pos = px + rw + location;
					line.setLine(
						pos, 0.20f*rw,
						pos, rw );
					g2d.draw(line);

					// number
					final int x = (int)( pos + 0.3f*fSize );
					final int y = (int)fSize + 1;
					g2d.drawString( Integer.toString( num ), x, y );

					// sub
					for( int jj=0; jj<subNum; jj++ )
					{
						final float pos_ = pos + 0.2f*(jj+1)*factor*offset;

						line.setLine(
							pos_, 0.75f*rw,
							pos_, rw );
						g2d.draw(line);
					}

				}

			}


			// perpendicular
			{
				final ArrayList numberList = new ArrayList();
				final float py = (float)pRect.getY();
				final float pOffset = - py;
				final float pOffsetInRulerUnit = pOffset/factor;
				final float endLocation = (float)vpRect.getHeight() + pOffset;
				int diff = (int)pOffsetInRulerUnit;
				if( diff%offset!=0 )
				{
					diff = (diff/offset)*offset;
				}

				int cnt = diff;
				while( true )
				{
//System.out.print(cnt+" ");
					final float location = factor*cnt;
					if( location > endLocation )
					{
						break;
					}

					numberList.add( new Integer(cnt) );
					cnt+=offset;
				}
//System.out.println();

				// draw
				for( int ii=0; ii<numberList.size(); ii++ )
				{

					// main
					final int num = ((Integer)numberList.get(ii)).intValue();
					final float location = num*factor;
					final float pos = py + rw + location;
					line.setLine(
						0.20f*rw, pos,
						rw, pos );
					g2d.draw(line);

					// number
					final int x = (int)( 0.20f*rw );
					int y = (int)( pos + fSize + 1 );
					char[] array = Integer.toString( num ).toCharArray();
					for( int jj=0; jj<array.length; jj++ )
					{
						Character c = new Character( array[jj] );
						g2d.drawString( c.toString(), x, y );
						y += (int)fSize;
					}

					// sub
					for( int jj=0; jj<subNum; jj++ )
					{
						final float pos_ = pos + 0.2f*(jj+1)*factor*offset;
						line.setLine(
							0.750f*rw, pos_,
							rw, pos_ );
						g2d.draw(line);
					}
				}

			}


			return true;
		}


	}




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


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


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

			return true;
		}


		public ArrayList getVisibleFigureList()
		{
			ArrayList 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 void setVisibleFigureList( final ArrayList 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;
		}

	}



}
