/* $Id: ProcessFlowView.java,v 1.3 2007/12/11 09:54:58 nito Exp $
 * 
 * Copyright (c)ARGO 21, Corporation. 2005, 2006.  All rights reserved.
 * 
 * This file is part of Nautica Workflow.
 * 
 *  Nautica Workflow is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 * 
 *  Nautica Workflow is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with Nautica Workflow; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 */
package jp.co.argo21.nautica.tool.wfd;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.SystemColor;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JOptionPane;

import jp.co.argo21.nautica.commons.swing.DialogUtilities;
import jp.co.argo21.nautica.commons.swing.Grid;
import jp.co.argo21.nautica.commons.util.IDGenerator;
import jp.co.argo21.nautica.commons.util.ResourceManager;
import jp.co.argo21.nautica.commons.util.StringUtils;
import jp.co.argo21.nautica.tool.wfd.element.ActivityElement;
import jp.co.argo21.nautica.tool.wfd.element.ActivityState;
import jp.co.argo21.nautica.tool.wfd.element.Element;
import jp.co.argo21.nautica.tool.wfd.element.PackageElement;
import jp.co.argo21.nautica.tool.wfd.element.ProcessElement;
import jp.co.argo21.nautica.tool.wfd.element.TransitionElement;
import jp.co.argo21.nautica.tool.wfd.element.VariableElement;
import jp.co.argo21.nautica.tool.wfd.element.impl.ActivityElementImpl;
import jp.co.argo21.nautica.tool.wfd.element.impl.TransitionElementImpl;
import jp.co.argo21.nautica.tool.wfd.expression.ConditionalVariable;
import jp.co.argo21.nautica.tool.wfd.sim.SimulatableActivityWrapper;
import jp.co.argo21.nautica.tool.wfd.sim.SimulationListener;
import jp.co.argo21.nautica.tool.wfd.sim.SimurateManager;

/**
 * フロー描画用ビュー
 * 
 * 
 * @author  Norihiro Itoh(ARGO 21 Corp.)
 * @version $Revision: 1.3 $
 * @since   Nautica Workflow 1.0
 */
public class ProcessFlowView
extends JComponent
implements MouseListener, MouseMotionListener, DropTargetListener, SimulationListener
{
	/** シリアルバージョンID */
	private static final long serialVersionUID = 1L;

	/** 編集用アダプタを表す定数 */
	public static final int POINT_CONTROLLER    = 0;
	/** 接続用アダプタを表す定数 */
	public static final int CONNECT_CONTROLLER  = 1;
	/** シミュレート用アダプタを表す定数 */
	public static final int SIMULATE_CONTROLLER = 2;

	/** 接続モードを表す定数 */
	public static final int CMODE_CONNECT    = 0;
	/** 接続解除モードを表す定数 */
	public static final int CMODE_DISCONNECT = 1;

	/** グリッド幅を表す定数 */
	public static final int GRID_SIZE     = 80;
	/** アイコンサイズを表す定数 */
	private static final int MAX_ICON_SIZE = 48;
	/** 内法を表す定数 */
	private static final int INSET_SIZE    = 9;
	/** 最小論理幅単位数を表す定数 */
	private static final int MIN_LOGICAL_WIDTH  = 10;
	/** 最小論理高さ単位数を表す定数 */
	private static final int MIN_LOGICAL_HEIGHT = 10;

	/** 同期レイヤーの背景色 */
	private static final Color LAYER_BACKGROUND = new Color(230, 255, 255, 64);
	/** 接続モード時の背景色 */
	private static final Color CONNECTION_BACKGROUND = new Color(255, 255, 230);
	/** シミュレーションモード時の背景色 */
	private static final Color SIMULATION_BACKGROUND = new Color(230, 255, 255);

	/** 描画に使用するフォント */
	private static final Font VIEW_FONT
		= new Font("MonoSpaced", Font.PLAIN, 10);

	/** 矢印用描画パス */
	private static GeneralPath ARROW_PATH;

	/** ドロップ使用としている矩形座標 */
	private Rectangle dropRect;
	/** カーソル位置の矩形座標 */
	private Rectangle cursorRect;
	/** 選択中の矩形座標 */
	private Rectangle selectedRect;

	/** 選択中のActivityElement */
	private ActivityElement selectedActivity;
	/** 接続元のActivityElement */
	private ActivityElement sourceActivity;
	/** 現在シミュレート対象となっているSimulatableActivityWrapper */
	private List<SimulatableActivityWrapper> simulatedActivities =
		new ArrayList<SimulatableActivityWrapper>();
	/** 現在待機中となっているSimulatableActivityWrapper */
	private Map<SimulatableActivityWrapper, SimulatableActivityWrapper> waitActivities =
		new Hashtable<SimulatableActivityWrapper, SimulatableActivityWrapper>();

	/** ビュー制御用のコントローラ */
	private ProcessFlowViewController[] controllers;

	/** 選択されているコントローラインデクス */
	private int controllerID = POINT_CONTROLLER;
	/** 現在の接続動作 */
	private int connectingMode = CMODE_CONNECT;

	/** 対象となるプロセス */
	private ProcessElement proc;

	/** 同期階層を管理するリスト */
	private ConcurrentLayerManager root;

	/** シミュレータ */
	private SimurateManager simulator;

	/**
	 * 矢印用描画パスの初期化
	 */
	static
	{
		// 矢印用描画パスの初期化
		ARROW_PATH = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
		ARROW_PATH.moveTo(0, 0);
		ARROW_PATH.lineTo(15, 5);
		ARROW_PATH.lineTo(15, -5);
		ARROW_PATH.closePath();
	}

	/**
	 * フロー描画用のビューを生成する。
	 * 
	 */
	public ProcessFlowView(ProcessElement proc)
	{
		this.proc = proc;
		int w = proc.getLogicalWidth();
		int h = proc.getLogicalHeight();

		if (w < MIN_LOGICAL_WIDTH) {
			w = MIN_LOGICAL_WIDTH;
			proc.setLogicalWidth(MIN_LOGICAL_WIDTH);
		} 
		if (h < MIN_LOGICAL_HEIGHT) {
			h = MIN_LOGICAL_HEIGHT;
			proc.setLogicalHeight(MIN_LOGICAL_HEIGHT);
		} 
		
		Dimension size = new Dimension(GRID_SIZE * w + 1, GRID_SIZE * h + 1);
		
		root = new ConcurrentLayerManager();
		root.initConcurrentLayer(proc.getActivities());
		
		simulator = new SimurateManager();
		
		controllers = new ProcessFlowViewController[3];
		controllers[0] = new PointerModeFlowViewController();
		controllers[1] = new ConnectionModeFlowViewController();
		controllers[2] = new SimulationModeFlowViewController();
			
		new DropTarget(this, this);
		setPreferredSize(size);
		setMinimumSize(size);
		addMouseListener(this);
		addMouseMotionListener(this);
	}

	/**
	 * フロー表示エリアを描画する。
	 *
	 * @param g 描画対象グラフィックス
	 */
	public void paintComponent(Graphics g)
	{
		Shape clip = g.getClip();

		Dimension size = this.getSize();

		//描画範囲以外の背景の描画
		g.setColor(Color.gray);
		g.fillRect(0, 0, size.width, size.height);

		Dimension psize = this.getPreferredSize();

		//描画範囲の背景の描画
		if (getControllerID() == CONNECT_CONTROLLER) {
			g.setColor(CONNECTION_BACKGROUND);
		} else if (getControllerID() == SIMULATE_CONTROLLER) {
			g.setColor(SIMULATION_BACKGROUND);
		} else {
			g.setColor(Color.white);
		}
		g.fillRect(0, 0, psize.width, psize.height);

		//レイヤーの背景描画
		root.paintLayerBackgroud(g, LAYER_BACKGROUND, GRID_SIZE);


		//選択中のアクティビティの描画
		if (selectedActivity != null) {
			paintActivityBackground(g, Color.pink, selectedActivity);
		}
		
		//シミュレート対象のアクティビティの背景描画
		if (getControllerID() == SIMULATE_CONTROLLER) {
			for (SimulatableActivityWrapper sa : simulatedActivities) {
				if (sa != null) {
					paintActivityBackground(g, Color.green, sa.getActivity());
				}
			}
		}

		//ビューの描画
		paintView(g);
						
		//カーソル矩形の描画
		if (cursorRect != null) {
			g.drawRect(cursorRect.x, cursorRect.y, cursorRect.width, cursorRect.height);
		}

		//レイヤーの枠描画
		root.paintLayerBorder(g, Color.blue, GRID_SIZE);

		//ドロップ矩形の描画
		g.setColor(Color.red);
		if (dropRect != null) {
			g.drawRect(dropRect.x, dropRect.y, dropRect.width, dropRect.height);
		}

		//選択中のアクティビティの描画
		if (selectedRect != null) {
			g.setColor(Color.red);
			g.drawRect(selectedRect.x, selectedRect.y, selectedRect.width, selectedRect.height);
		}

		//シミュレート対象の遷移描画
		if (getControllerID() == SIMULATE_CONTROLLER) {
			for (SimulatableActivityWrapper sa : simulatedActivities) {
				TransitionElement tr = sa.getTransition();
				if (tr != null) {
					paintTransition(g, Color.blue, tr.getSource(), tr.getDestination());
				}
			}
		}
			
		//接続線の描画
		if (getControllerID() == CONNECT_CONTROLLER) {
			if (sourceActivity != null) {
				paintConnecting(g);
			}
		}

		g.setClip(clip);
		g.dispose();
	}

	/**
	 * フロー表示エリアを描画する。
	 * これは印刷時にも使用する。
	 *
	 * @param g 描画対象グラフィックス
	 */
	public void paintView(Graphics g)
	{
		Shape clip = g.getClip();

		g.setFont(VIEW_FONT);

		Dimension psize = this.getPreferredSize();

		//描画範囲のグリッドの描画
		Grid.draw(new Rectangle(psize), g, GRID_SIZE);
		g.clipRect(0, 0, psize.width, psize.height);

		g.setColor(Color.black);
			
		//アクティビティの描画
		List<ActivityElement> acts = proc.getActivities();
		for (ActivityElement act : acts) {
			paintActivity(g, act);
		}
			
		//遷移の描画
		List<TransitionElement> trs = proc.getTransitions();
		for (TransitionElement tr : trs) {
			paintTransition(g, Color.black, tr.getSource(), tr.getDestination());
		}

		g.setClip(clip);
	}

	/**
	 * 指定されたアクティビティ背景を描画する。
	 * 
	 * @param g   描画対象グラフィックス
	 * @param c   描画色
	 * @param act アクティビティ
	 */
	private void paintActivityBackground(Graphics g, Color c, ActivityElement act)
	{
		if (act == null) return;

		int ax = act.getLogicalX();
		int ay = act.getLogicalY();

		int x = GRID_SIZE * ax;
		int y = GRID_SIZE * ay;
		int w = GRID_SIZE;
		int h = GRID_SIZE;

		g.setColor(c);
		g.fillRect(x, y, w, h);
	}

	/**
	 * 指定されたアクティビティを描画する。
	 * 
	 * @param g  描画対象グラフィックス
	 * @param act アクティビティ
	 */
	private void paintActivity(Graphics g, ActivityElement act)
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		String name = act.getName();
		if (StringUtils.checkNull(name)) {
			name = rm.getResource("ProcessFlowView.noname");
		} 

		//アクティビティアイコンの描画
		String type = act.getActivityState().getIconName();
		Icon icon = IconManager.getIcon(type.toString());

		int ax = act.getLogicalX();
		int ay = act.getLogicalY();

		int x = GRID_SIZE * ax;
		int y = GRID_SIZE * ay;
		int w = GRID_SIZE;
		//int h = GRID_SIZE;

		int iconW = icon.getIconWidth();
		int iconH = icon.getIconHeight();
		int iconX = x + ((w - iconW) / 2);
		int iconY = y + 9;
		icon.paintIcon(this, g, iconX, iconY);

		//アクティビティ名の描画
		FontMetrics metrics = g.getFontMetrics();
		int fontH = metrics.getHeight();
		int descent = metrics.getDescent();
		int lineW = metrics.stringWidth(name);
		int dx = x + ((w - lineW) / 2);
		int dy = y + 9 + iconH;
		if (dx < 0) dx = 0;
		dy = dy + fontH - descent;
		g.setColor(SystemColor.textText);
		g.drawString(name, dx, dy);
	}

	/**
	 * 指定されたアクティビティ間の遷移を描画する。
	 * 
	 * @param g   描画対象グラフィックス
	 * @param c   描画色
	 * @param src 遷移元アクティビティ
	 * @param dst 遷移先アクティビティ
	 */
	private void paintTransition(Graphics g, Color c, ActivityElement src, ActivityElement dst)
	{
		if (src == null) return;
		if (dst == null) return;

		int sx = src.getLogicalX();
		int sy = src.getLogicalY();
		int dx = dst.getLogicalX();
		int dy = dst.getLogicalY();

		Point sp = getSourcePoint(sx, sy, dx, dy);
		Point dp = getDestinationPoint(sx, sy, dx, dy);

		//遷移線の描画
		paintArrowLine(g, c, sp.x, sp.y, dp.x, dp.y);
	}

	/**
	 * 接続を描画する。
	 *
	 * @param	g		描画対象グラフィックス
	 */
	private void paintConnecting(Graphics g)
	{
		if (controllerID != CONNECT_CONTROLLER) return;
		if (sourceActivity == null) return;
		if (cursorRect == null) return;

		int sx = sourceActivity.getLogicalX();
		int sy = sourceActivity.getLogicalY();
		int dx = cursorRect.x / GRID_SIZE;
		int dy = cursorRect.y / GRID_SIZE;

		if (getConnectingMode() == CMODE_CONNECT) {
			Point sp = getSourcePoint(sx, sy, dx, dy);
			Point dp = getDestinationPoint(sx, sy, dx, dy);
			paintArrowLine(g, Color.gray, sp.x, sp.y, dp.x, dp.y);
		} else {
			Point sp = getSourcePoint(sx, sy, dx, dy);
			Point dp = getDestinationPoint(sx, sy, dx, dy);
			paintCrossLine(g, Color.gray, sp.x, sp.y, dp.x, dp.y);
		}
	}

	/**
	 * 矢印線を描画する。
	 *
	 * @param g  描画対象グラフィックス
	 * @param c  描画色
	 * @param sx 開始X座標
	 * @param sy 開始Y座標
	 * @param dx 終了X座標
	 * @param dy 終了Y座標
	 */
	private void paintArrowLine(Graphics g, Color c, int sx, int sy, int dx, int dy)
	{
		Graphics2D g2 = (Graphics2D)g;

		//現在の座標変換行列を保存
		AffineTransform tr = g2.getTransform();

		g2.setColor(c);
		g2.drawLine(sx, sy, dx, dy);

		//矢印を描画
		g2.translate(dx, dy);
		double theta = Math.atan2(((double)(sy - dy)), ((double)(sx - dx)));
		g2.rotate(theta);
		g2.fill(ARROW_PATH);
		g2.draw(ARROW_PATH);

		g2.setTransform(tr);
	}

	/**
	 * ×印付矢印線を描画する。
	 *
	 * @param g  描画対象グラフィックス
	 * @param c  描画色
	 * @param sx 開始X座標
	 * @param sy 開始Y座標
	 * @param dx 終了X座標
	 * @param dy 終了Y座標
	 */
	private void paintCrossLine(Graphics g, Color c, int sx, int sy, int dx, int dy)
	{
		paintArrowLine(g, c, sx, sy, dx, dy);

		Graphics2D g2 = (Graphics2D)g;

		Stroke stroke = g2.getStroke();

		g2.setStroke(new BasicStroke(3));
		int x = sx + ((dx - sx) / 2);
		int y = sy + ((dy - sy) / 2);
		g2.setColor(Color.red);
		g2.drawLine(x - 7, y - 7, x + 7, y + 7);
		g2.drawLine(x + 7, y - 7, x - 7, y + 7);

		g2.setStroke(stroke);
	}

	/**
	 * 接続モードを返す。
	 *
	 * @return 接続モード(CMODE_CONNECT/CMODE_DISCONNECT)
	 */
	private int getConnectingMode()
	{
		return connectingMode;
	}

	/**
	 * 接続モードを設定する。
	 *
	 * @param mode 接続モード(CMODE_CONNECT/CMODE_DISCONNECT)
	 */
	private void setConnectingMode(int mode)
	{
		connectingMode = mode;
	}

	/**
	 * 接続元の描画開始点を算出する。
	 *
	 * @param sx 開始X座標
	 * @param sy 開始Y座標
	 * @param dx 終了X座標
	 * @param dy 終了Y座標
	 * @return 描画開始点
	 */
	private Point getSourcePoint(int sx, int sy, int dx, int dy)
	{
		int x = (sx * GRID_SIZE) + (GRID_SIZE / 2);
		int y = (sy * GRID_SIZE) + (GRID_SIZE / 2);
		if (sx < dx) {
			x = (sx * GRID_SIZE) + (GRID_SIZE - ((GRID_SIZE - MAX_ICON_SIZE) / 2));
			y = (sy * GRID_SIZE) + INSET_SIZE + (MAX_ICON_SIZE / 2);
		} else if (sx > dx) {
			x = (sx * GRID_SIZE) + ((GRID_SIZE - MAX_ICON_SIZE) / 2);
			y = (sy * GRID_SIZE) + INSET_SIZE + (MAX_ICON_SIZE / 2);
		} else {
			if (sy < dy) {
				y = (sy * GRID_SIZE) + GRID_SIZE - INSET_SIZE;
			} else if (sy > dy) {
				y = (sy * GRID_SIZE) + INSET_SIZE;
			}
		}
		return new Point(x, y);
	}

	/**
	 * 接続元の描画終了点を算出する。
	 *
	 * @param sx 開始X座標
	 * @param sy 開始Y座標
	 * @param dx 終了X座標
	 * @param dy 終了Y座標
	 * @return 描画終了点
	 */
	private Point getDestinationPoint(int sx, int sy, int dx, int dy)
	{
		int x = (dx * GRID_SIZE) + (GRID_SIZE / 2);
		int y = (dy * GRID_SIZE) + (GRID_SIZE / 2);
		if (sx < dx) {
			x = (dx * GRID_SIZE) + ((GRID_SIZE - MAX_ICON_SIZE) / 2);
			y = (dy * GRID_SIZE) + INSET_SIZE + (MAX_ICON_SIZE / 2);
		} else if (sx > dx) {
			x = (dx * GRID_SIZE) + (GRID_SIZE - ((GRID_SIZE - MAX_ICON_SIZE) / 2));
			y = (dy * GRID_SIZE) + INSET_SIZE + (MAX_ICON_SIZE / 2);
		} else {
			if (sy < dy) {
				y = (dy * GRID_SIZE) + INSET_SIZE;
			} else if (sy > dy) {
				y = (dy * GRID_SIZE) + GRID_SIZE - INSET_SIZE;
			}
		}
		return new Point(x, y);
	}
		
	/**
	 * マウスカーソルの座標からアクティビティを特定する。
	 * 
	 * @param mx マウスのX座標
	 * @param my マウスのY座標
	 * @return アクティビティ
	 */
	private ActivityElement getActivity(int mx, int my)
	{
		int lx = mx / GRID_SIZE;
		int ly = my / GRID_SIZE;

		List<ActivityElement> acts = proc.getActivities();
		for (ActivityElement a : acts) {
			int x = a.getLogicalX();
			int y = a.getLogicalY();
			if (x == lx && y == ly) {
				return a;
			}
		}
		return null;
	}
	
	/**
	 * アクティビティのドロップ処理を行う。
	 * 
	 * @param name ドロップされたアクティビティ項目名
	 * @param mx マウスX座標
	 * @param my マウスY座標
	 * @return ドロップの成否
	 */
	private boolean dropActivity(String name, int mx, int my)
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		if (checkDroppingName(name) == false) {
			DialogUtilities.showError(rm.getResource("message.error.0021"));
			return false;
		}

		Dimension psize = this.getPreferredSize();

		if ((mx >= psize.width) || (my >= psize.height)) {
			DialogUtilities.showError(rm.getResource("message.error.0022"));
			return false;
		}
	
		if (getActivity(mx, my) != null) {
			DialogUtilities.showError(rm.getResource("message.error.0023"));
			return false;
		}

		boolean result = addActivity(name, mx, my);

		this.repaint();
		return result;
	}
	
	/**
	 * アクティビティを追加する。
	 * 
	 * @param name アクティビティ項目名
	 * @param mx マウスX座標
	 * @param my マウスY座標
	 */
	private boolean addActivity(String name, int mx, int my)
	{
		if (name.equals(WorkflowDesignerConstants.PAL_CONCURRENT)) {
			return addConcurrenceActivity(name, mx, my);
		} else {
			return addGeneralActivity(name, mx, my);
		}
	}
	
	/**
	 * 並行処理用アクティビティを追加する。
	 * 
	 * @param name アクティビティ項目名
	 * @param mx マウスX座標
	 * @param my マウスY座標
	 */
	private boolean addConcurrenceActivity(String name, int mx, int my)
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		int mx2 = mx + GRID_SIZE;

		Dimension psize = this.getPreferredSize();

		if ((mx2 >= psize.width) || (my >= psize.height)) {
			DialogUtilities.showError(rm.getResource("message.error.0024"));
			return false;
		}

		if (getActivity(mx2, my) != null) {
			DialogUtilities.showError(rm.getResource("message.error.0025"));
			return false;
		}

		int x1 = mx / GRID_SIZE;
		int y1 = my / GRID_SIZE;
		int x2 = mx2 / GRID_SIZE;
		int y2 = my / GRID_SIZE;
		
		ConcurrentLayer layer1 = root.getReceivableLayer(x1, y1);
		ConcurrentLayer layer2 = root.getReceivableLayer(x2, y2);
		
		if (layer1 != layer2) {
			DialogUtilities.showError(rm.getResource("message.error.0024"));
			return false;
		}
		
		ActivityState state1 = ActivityStateFactory.getInstance().createElement(ActivityElement.CONCUR_START);
		ActivityState state2 = ActivityStateFactory.getInstance().createElement(ActivityElement.CONCUR_END);
		if (state1 == null || state2 == null) return false;
		
		ActivityElement act1 = new ActivityElementImpl(state1);
		ActivityElement act2 = new ActivityElementImpl(state2);
		String blockID = IDGenerator.generate(ActivityElement.BLOCK_ID_PREFIX);
		act1.setBlockID(blockID);
		act2.setBlockID(blockID);

		ConcurrentLayer newLayer = new ConcurrentLayer(layer1, act1, act2);
		if (layer1 == null) root.addLayer(newLayer);
		else layer1.addLayer(newLayer);

		act1.setLogicalX(x1);
		act1.setLogicalY(y1);
		act2.setLogicalX(x2);
		act2.setLogicalY(y2);

		proc.getActivities().add(act1);
		proc.getActivities().add(act2);
		
		
		return true;
	}
	
	/**
	 * 通常アクティビティを追加する。
	 * 
	 * @param name アクティビティ項目名
	 * @param mx マウスX座標
	 * @param my マウスY座標
	 */
	private boolean addGeneralActivity(String name, int mx, int my)
	{
		//ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		ActivityState state = ActivityStateFactory.getInstance().createElementByPalette(name);
		if (state == null) return false;

		int x = mx / GRID_SIZE;
		int y = my / GRID_SIZE;
		
		ConcurrentLayer layer = root.getReceivableLayer(x, y);
		
		ActivityElement act = new ActivityElementImpl(state);
		act.setLogicalX(x);
		act.setLogicalY(y);

		if (layer != null) layer.addLayerElement(act);
		proc.getActivities().add(act);

		return true;
	}

	/**
	 * アクティビティを削除する。
	 * 
	 * @param act アクティビティ
	 */
	private void removeActivity(ActivityElement act)
	{
		if (act == null) return;

		//削除対象のアクティビティに関連する遷移を検索する。
		List<TransitionElement> trs = proc.getTransitions();
		List<TransitionElement> tempTrs = new ArrayList<TransitionElement>();		
		for (TransitionElement tr: trs) {
			if (tr.getSource() == act || tr.getDestination() == act) {
				tempTrs.add(tr);
			}
		}

		//削除対象のアクティビティに関連する遷移を削除する。
		for (TransitionElement tempTr: tempTrs) {
			trs.remove(tempTr);
		}

		//削除対象のアクティビティを削除する。
		List<ActivityElement> acts = proc.getActivities();
		acts.remove(act);
		selectedActivity = null;
		inspectSelectedActivity(false);
		
		repaint();
	}
	
	/**
	 * 開始アクティビティを返す。
	 * 
	 * @return	開始アクティビティ
	 */
	private ActivityElement getStartActivity()
	{
		if (proc != null) {
			for (ActivityElement act : proc.getActivities()) {
				if (act.getType().equals(ActivityElement.START)) {
					return act;
				}
			}
		}
		return null;
	}
		
	/**
	 * ドロップされたアクティビティ名をチェックする。
	 * 
	 * @param name アクティビティ名
	 * @return 判定結果
	 */
	private boolean checkDroppingName(String name)
	{
		if (name == null) return false;
		for (int i = 0; i < WorkflowDesignerConstants.DROPPABLE_ITEMS.length; i++) {
			if (WorkflowDesignerConstants.DROPPABLE_ITEMS[i].equals(name)) {
				return true;
			}
		}
		return false;
	}
		
	/**
	 * 遷移を追加する。
	 * 
	 * @param src 遷移元のアクティビティ
	 * @param dst 遷移先のアクティビティ
	 */
	private void addTransition(ActivityElement src, ActivityElement dst)
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		if (src == dst) {
			//自分自身には遷移は張らない。キャンセル動作のため、ダイアログは不要。
			return;
		}

		if (dst.getType().equals(ActivityElement.START)) {
			DialogUtilities.showError(rm.getResource("message.error.0032"));
			return;
		}

		List<ActivityElement> acts = proc.getActivities();
		boolean existSrc = false;
		boolean existDst = false;
		for (ActivityElement a : acts) {
			if (a == src) existSrc = true;
			else if (a == dst) existDst = true;
		}
		if (existSrc == false || existDst == false) {
			DialogUtilities.showError(rm.getResource("message.error.0033"));
			return;
		}

		//対象のアクティビティに関連する遷移を検索する。
		List<TransitionElement> trs = proc.getTransitions();
		for (TransitionElement tr : trs) {
			if (tr.getSource() == src && tr.getDestination() == dst) {
				DialogUtilities.showError(rm.getResource("message.error.0034"));
				return;
			}
		}
		
		if (checkTransitionLayer(src, dst)) {
			TransitionElement newTr = new TransitionElementImpl(src, dst);
			trs.add(newTr);
		}
	}

	/**
	 * 同期階層と遷移のチェックを行う。
	 * 
	 * @param src	遷移元アクティビティ
	 * @param dst	遷移先アクティビティ
	 * @return	遷移可能ならtrue
	 */
	private boolean checkTransitionLayer(ActivityElement src, ActivityElement dst)
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		ConcurrentLayer layer1 = root.getContainLayer(src);
		ConcurrentLayer layer2 = root.getContainLayer(dst);

		if (layer1 == null && layer2 == null) {
		} else if (layer1 == null) {
			if (dst.getType().equals(ActivityElement.CONCUR_START)) {
				ConcurrentLayer pl = layer2.getParentLayer();
				if (pl != null) {
					DialogUtilities.showError(rm.getResource("message.error.0037"));
					return false;
				}
			} else {
				DialogUtilities.showError(rm.getResource("message.error.0036"));
				return false;
			}
		} else if (layer2 == null) {
			if (src.getType().equals(ActivityElement.CONCUR_END)) {
				ConcurrentLayer pl = layer1.getParentLayer();
				if (pl != null) {
					DialogUtilities.showError(rm.getResource("message.error.0037"));
					return false;
				}
			} else {
				DialogUtilities.showError(rm.getResource("message.error.0036"));
				return false;
			}
		} else if (layer1 != layer2) {
			if (dst.getType().equals(ActivityElement.CONCUR_START)) {		
				ConcurrentLayer pl = layer2.getParentLayer();
				if (pl != layer1) {
					DialogUtilities.showError(rm.getResource("message.error.0037"));
					return false;
				}
			} else if (src.getType().equals(ActivityElement.CONCUR_END)) {
				ConcurrentLayer pl = layer1.getParentLayer();
				if (pl != layer2) {
					DialogUtilities.showError(rm.getResource("message.error.0037"));
					return false;
				}
			} else {
				DialogUtilities.showError(rm.getResource("message.error.0036"));
				return false;
			}
		} else {
			if (dst.getType().equals(ActivityElement.CONCUR_START)) {		
				DialogUtilities.showError(rm.getResource("message.error.0038"));
				return false;
			} else if (src.getType().equals(ActivityElement.CONCUR_END)) {
				DialogUtilities.showError(rm.getResource("message.error.0039"));
				return false;
			}
		}
		return true;
	}
		
	/**
	 * 遷移を削除する。
	 * 
	 * @param src 遷移元のアクティビティ
	 * @param dst 遷移先のアクティビティ
	 */
	private void removeTransition(ActivityElement src, ActivityElement dst)
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		//対象のアクティビティに関連する遷移を検索する。
		List<TransitionElement> trs = proc.getTransitions();
		TransitionElement rmTr = null;
		for (TransitionElement tr : trs) {
			if (tr.getSource() == src && tr.getDestination() == dst) {
				rmTr = tr;
				break;
			}
		}
		if (rmTr == null) {
			DialogUtilities.showError(rm.getResource("message.error.0035"));
			return;
		}
		trs.remove(rmTr);
	}

	/**
	 * 同期階層と配置位置のチェックを行う。
	 * 
	 * @param l	同期階層
	 * @param elem	要素
	 * @param lx	論理X座標
	 * @param ly	論理Y座標
	 * @return	配置可能ならtrue
	 */	
	private boolean checkLocation(ConcurrentLayer l, ActivityElement elem, int lx, int ly)
	{
		if (l == null) return true;
		ActivityElement start = l.getStartElement();
		ActivityElement end = l.getEndElement();
		ConcurrentLayer parentLayer = l.getParentLayer();
		int sx = start.getLogicalX();
		int sy = start.getLogicalY();
		int ex = end.getLogicalX();
		int ey = end.getLogicalY();
		Point mup = l.getMinimumUpperPoint();
		Point mlp = l.getMinimumLowerPoint();
		if (lx == sx && ly == sy) return false;
		if (lx == ex && ly == ey) return false;
		
		if (elem == start) {
			if (parentLayer != null) {
				int psx = parentLayer.getStartElement().getLogicalX();
				int psy = parentLayer.getStartElement().getLogicalY();
				int pex = parentLayer.getEndElement().getLogicalX();
				int pey = parentLayer.getEndElement().getLogicalY();
				if (lx == psx && ly == psy) return false;
				if (lx == pex && ly == pey) return false;
				if (lx < psx || lx > ex) return false;
				if (ly < psy || ly > ey) return false;
			}
			if (lx > mup.x || ly > mup.y) return false;
		} else if (elem == end) {
			if (parentLayer != null) {
				int psx = parentLayer.getStartElement().getLogicalX();
				int psy = parentLayer.getStartElement().getLogicalY();
				int pex = parentLayer.getEndElement().getLogicalX();
				int pey = parentLayer.getEndElement().getLogicalY();
				if (lx == psx && ly == psy) return false;
				if (lx == pex && ly == pey) return false;
				if (lx < sx || lx > pex) return false;
				if (ly < sy || ly > pey) return false;
			}
			if (lx < mlp.x || ly < mlp.y) return false;
		} else {
			if (lx < sx || lx > ex) return false;
			if (ly < sy || ly > ey) return false;
		}
		return true;
	}

	/**
	 * 最小のビューサイズを返す。
	 * 
	 * @return	サイズ
	 */	
	private Dimension getMinimumViewSize()
	{
		int lw = MIN_LOGICAL_WIDTH;
		int lh = MIN_LOGICAL_HEIGHT;
		if (proc != null) {
			for (ActivityElement act : proc.getActivities()) {
				if (act.getLogicalX() >= lw) lw = act.getLogicalX() + 1;
				if (act.getLogicalY() >= lh) lh = act.getLogicalY() + 1;
			}
		}
		return new Dimension(GRID_SIZE * lw + 1, GRID_SIZE * lh + 1);
	}

	/**
	 * 現在のビューコントローラIDを返す。
	 * 
	 * @return	ビューコントローラID
	 */
	public int getControllerID()
	{
		return controllerID;
	}

	/**
	 * ビューコントローラIDを設定する。
	 * 
	 * @param i	ビューコントローラID
	 */
	public void setControllerID(int i)
	{
		controllerID = i;
		setSelectedActivity(null);
		sourceActivity = null;
		selectedRect = null;
		setToolTipText(null);
		inspectSelectedActivity(false);
		simulatedActivities.clear();
		waitActivities.clear();
		if (getControllerID() == SIMULATE_CONTROLLER) {
			ActivityElement act = getStartActivity();
			if (act != null) simulatedActivities.add(new SimulatableActivityWrapper(act, null));
		}
		
		repaint();
	}

	/**
	 * 選択されているアクティビティを返す。
	 * 
	 * @return	アクティビティ
	 */
	public ActivityElement getSelectedActivity()
	{
		return selectedActivity;
	}

	/**
	 * 指定されたアクティビティを選択状態にする。
	 * 
	 * @param element	アクティビティ
	 */
	public void setSelectedActivity(ActivityElement element)
	{
		WorkflowDesignerFrame frame = WorkflowDesignerManager.getMainFrame();
		
		selectedActivity = element;
		if (selectedActivity == null) {
			frame.setEnableCopyAction(false);
			frame.setEnableDeleteAction(false);
			return;
		} 

		String type = selectedActivity.getType();
		if (type.equals(ActivityElement.START)) {
			frame.setEnableCopyAction(false);
			frame.setEnableDeleteAction(false);
		} else if (type.equals(ActivityElement.CONCUR_START)
			|| type.equals(ActivityElement.CONCUR_END)
			|| type.equals(ActivityElement.END)) {
				frame.setEnableCopyAction(false);
				frame.setEnableDeleteAction(true);
		} else {
			frame.setEnableCopyAction(true);
			frame.setEnableDeleteAction(true);
		}
	}

	/**
	 * 選択されているアクティビティのインスペクタをロードする。
	 * 
	 * @param withShowing	ロードと同時に表示を行う場合はtrue
	 */
	public void inspectSelectedActivity(boolean withShowing)
	{
		DesktopPane desktop = WorkflowDesignerManager.getMainFrame().getDesktop();
		ProcessDesignFrame frame = desktop.getCurrentProcessDesignFrame();
		if (frame == null) return;
		frame.setInspector(selectedActivity);
		if (withShowing) frame.showInspector();
	}

	/**
	 * フレームが活性化した場合の処理。
	 */	
	public void notifyFrameIsActivated()
	{
		setSelectedActivity(selectedActivity);
	}

	/**
	 * フレームが非活性化した場合の処理。
	 */	
	public void notifyFrameIsDeactivated()
	{
		sourceActivity = null;
	}

	/**
	 * 編集エリアを横に一単位広げる。
	 */
	public void expandWidth()
	{
		Dimension d = getPreferredSize();
		int newWidth = d.width + GRID_SIZE;
		d.setSize(newWidth, d.height);
		setPreferredSize(d);
		setSize(d);
		proc.setLogicalWidth(newWidth / GRID_SIZE);
		updateUI();
	}

	/**
	 * 編集エリアを縦に一単位広げる。
	 */
	public void expandHeight()
	{
		Dimension d = getPreferredSize();
		int newHeight = d.height + GRID_SIZE;
		d.setSize(d.width, newHeight);
		setPreferredSize(d);
		setSize(d);
		proc.setLogicalHeight(newHeight / GRID_SIZE);
		updateUI();
	}

	/**
	 * 編集エリアを横に一単位縮める。
	 */
	public void reduceWidth()
	{
		Dimension d = getPreferredSize();
		Dimension md = getMinimumViewSize();
		
		int newWidth = d.width - GRID_SIZE;
		if (newWidth >= md.width) {
			d.setSize(newWidth, d.height);
			setPreferredSize(d);
			setSize(d);
			proc.setLogicalWidth(newWidth / GRID_SIZE);
			updateUI();
		}
	}

	/**
	 * 編集エリアを縦に一単位縮める。
	 */
	public void reduceHeight()
	{
		Dimension d = getPreferredSize();
		Dimension md = getMinimumViewSize();
		int newHeight = d.height - GRID_SIZE;
		if (newHeight >= md.height) {
			d.setSize(d.width, newHeight);
			setPreferredSize(d);
			setSize(d);
			proc.setLogicalHeight(newHeight / GRID_SIZE);
			updateUI();
		}
	}

	/**
	 * アクティビティ切り取り処理。
	 */
	public void cut()
	{
		if (selectedActivity == null) return;
		
		WorkflowDesignerFrame frame = WorkflowDesignerManager.getMainFrame();
		if (frame.getEnableCopyAction() == false) return;

		frame.setPastable(selectedActivity);
		delete();
		repaint();
	}

	/**
	 * アクティビティコピー処理。
	 */
	public void copy()
	{
		if (selectedActivity == null) return;

		WorkflowDesignerFrame frame = WorkflowDesignerManager.getMainFrame();
		if (frame.getEnableCopyAction() == false) return;

		Element pastable = selectedActivity.copy();
		frame.setPastable(pastable);
		repaint();
	}

	/**
	 * アクティビティペースト処理。
	 */
	public void paste()
	{
		if (selectedRect == null) return;

		WorkflowDesignerFrame frame = WorkflowDesignerManager.getMainFrame();
		if (frame.getEnablePasteAction() == false) return;

		Element pastable = frame.getPastable();
		
		if (pastable == null) return;
		
		if (pastable instanceof ActivityElement) {
			ActivityElement act = (ActivityElement)pastable;
			int lx = selectedRect.x / ProcessFlowView.GRID_SIZE;
			int ly = selectedRect.y / ProcessFlowView.GRID_SIZE;
			act.setLogicalX(lx);
			act.setLogicalY(ly);
			proc.addActivity(act);
			setSelectedActivity(act);
			ConcurrentLayer layer = root.getReceivableLayer(lx, ly);
			if (layer != null) layer.addLayerElement(act);
			frame.setPastable(null);
		}
		repaint();
	}

	/**
	 * アクティビティ削除処理。
	 */
	public void delete()
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		WorkflowDesignerFrame frame = WorkflowDesignerManager.getMainFrame();
		if (frame.getEnableDeleteAction() == false) return;

		if (selectedActivity == null) return;
		
		String type = selectedActivity.getType();
		if (type.equals(ActivityElement.CONCUR_START)
			|| type.equals(ActivityElement.CONCUR_END)) {
				int result = DialogUtilities.showOkCancelConfirm(rm.getResource("message.confirm.0001"));
				if (result != JOptionPane.OK_OPTION) return;
				
				ConcurrentLayer l = root.getContainLayer(selectedActivity);
				List<ActivityElement> temp = new ArrayList<ActivityElement>();
				l.collectAllLayerElement(temp);
				for (ActivityElement e : temp) {
					removeActivity(e);
				}
				root.removeLayer(l);
		} else {
			root.removeLayerElement(selectedActivity);
		}
		
		removeActivity(selectedActivity);
		setSelectedActivity(null);
		repaint();
	}

	/*
	 * インタフェースSimulationListener実装
	 */
	/**
	 * シミュレーションが開始されたことを反映する。
	 * 
	 * @param act	アクティビティラッパー
	 * @see jp.co.argo21.nautica.tool.wfd.sim.SimulationListener#simulationStarted(jp.co.argo21.nautica.tool.wfd.sim.SimulatableActivityWrapper)
	 */
	public void simulationStarted(SimulatableActivityWrapper act)
	{
		repaint();
	}

	/**
	 * シミュレーションが進展したことを反映する。
	 * 
	 * @param sa	アクティビティラッパー
	 * @param trs	シミュレートの結果、認識された遷移一覧
	 * @see jp.co.argo21.nautica.tool.wfd.sim.SimulationListener#simulationProgressed(jp.co.argo21.nautica.tool.wfd.sim.SimulatableActivityWrapper, java.util.List)
	 */
	public void simulationProgressed(SimulatableActivityWrapper sa, List<TransitionElement> trs)
	{
		if (trs == null) return;

		simulatedActivities.remove(sa);
		for (TransitionElement tr : trs)  {
			ActivityElement act = tr.getDestination();
			
			boolean hasBlockElement = false;
			SimulatableActivityWrapper top = null;
			if (act.getType().equals(ActivityElement.CONCUR_END)) {
				top = sa.peekTopBlockWrapper();
				hasBlockElement = checkHasBlockElement(top);
			}
			
			SimulatableActivityWrapper newSimact
				= new SimulatableActivityWrapper(act, tr, sa.getBlockStack());
			if (hasBlockElement) {
				waitActivities.put(newSimact, top);
			}
			simulatedActivities.add(newSimact);
		}

		repaint();
	}

	/**
	 * アクティビティラッパーがブロックされているかどうかを返す。
	 * 
	 * @param sa	アクティビティラッパー
	 * @return	持つ場合には、true
	 */	
	private boolean checkHasBlockElement(SimulatableActivityWrapper sa)
	{
		for (SimulatableActivityWrapper w : simulatedActivities) {
			if (w.searchBlockWrapper(sa) > -1) return true;
		}
		return false;
	}

	/**
	 * 指定されたアクティビティラッパーに対するブロックを削除する。
	 * 
	 * @param bk	ブロックアクティビティラッパー
	 * @param act	アクティビティラッパー
	 */	
	private void removeBlock(SimulatableActivityWrapper bk, SimulatableActivityWrapper act)
	{
		Set<Entry<SimulatableActivityWrapper, SimulatableActivityWrapper>> wset = waitActivities.entrySet();
		for (Entry<SimulatableActivityWrapper, SimulatableActivityWrapper> entry : wset) {
			if (entry.getValue() == bk) {
				waitActivities.remove(entry.getKey());
			}
		}
		List<SimulatableActivityWrapper> temp = new ArrayList<SimulatableActivityWrapper>();
		for (SimulatableActivityWrapper w : simulatedActivities) {
			if (w  == act) continue;
			else if (w.getActivity() == act.getActivity()) temp.add(w);
		}
		for (SimulatableActivityWrapper w : temp) {
			simulatedActivities.remove(w);
		}
		
	}

	/**
	 * シミュレーションが完了したことを反映する。
	 * 
	 * @param act	アクティビティラッパー
	 * @see jp.co.argo21.nautica.tool.wfd.sim.SimulationListener#simulationFinished(jp.co.argo21.nautica.tool.wfd.sim.SimulatableActivityWrapper)
	 */
	public void simulationFinished(SimulatableActivityWrapper act)
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		DialogUtilities.showInfo(rm.getResource("message.info.0001"));
		repaint();
	}

	/**
	 * シミュレーションが中断されたことを反映する。
	 * 
	 * @param act	アクティビティラッパー
	 * @see jp.co.argo21.nautica.tool.wfd.sim.SimulationListener#simulationTerminated(jp.co.argo21.nautica.tool.wfd.sim.SimulatableActivityWrapper)
	 */
	public void simulationTerminated(SimulatableActivityWrapper act)
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		DialogUtilities.showInfo(rm.getResource("message.info.0002"));
		repaint();
	}

	/*
	 * インタフェースMouseListener実装
	 */
	/**
	 * マウスクリックを判定する。
	 *
	 * @param e マウスイベント
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	public void mouseClicked(MouseEvent e)
	{
		controllers[controllerID].mouseClicked(e);
	}

	/**
	 * マウス押下を判定する。
	 *
	 * @param e マウスイベント
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	public void mousePressed(MouseEvent e)
	{
		controllers[controllerID].mousePressed(e);
	}

	/**
	 * マウスの押下解除を判定する。
	 *
	 * @param e マウスイベント
	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	public void mouseReleased(MouseEvent e)
	{
		controllers[controllerID].mouseReleased(e);
	}

	/**
	 * マウスの侵入を判定する。
	 *
	 * @param e マウスイベント
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	public void mouseEntered(MouseEvent e)
	{
		controllers[controllerID].mouseEntered(e);
	}

	/**
	 * マウスの脱出を判定する。
	 *
	 * @param e マウスイベント
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	public void mouseExited(MouseEvent e)
	{
		controllers[controllerID].mouseExited(e);
	}

	/**
	 * マウスドラッグを判定する。
	 *
	 * @param	e		マウスイベント
	 * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
	 */
	public void mouseDragged(MouseEvent e)
	{
		controllers[controllerID].mouseDragged(e);
	}

	/**
	 * マウス移動を判定する。
	 *
	 * @param	e		マウスイベント
	 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
	 */
	public void mouseMoved(MouseEvent e)
	{
		controllers[controllerID].mouseMoved(e);
	}

	/**
	 * ドラッグがビュー上に入った場合に呼ばれる。
	 * 
	 * @param e DropTargetDragEvent
	 * @see java.awt.dnd.DropTargetListener#dragEnter(java.awt.dnd.DropTargetDragEvent)
	 */
	public void dragEnter(DropTargetDragEvent e)
	{
		e.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
	}

	/**
	 * ドラッグがビューから出た場合に呼ばれる。
	 * 
	 * @param e DropTargetEvent
	 * @see java.awt.dnd.DropTargetListener#dragExit(java.awt.dnd.DropTargetEvent)
	 */
	public void dragExit(DropTargetEvent e)
	{
		dropRect = null;
		repaint();
	}

	/**
	 * ドラッグがビュー上にある場合に呼ばれる。
	 * 
	 * @param e DropTargetDragEvent
	 * @see java.awt.dnd.DropTargetListener#dragOver(java.awt.dnd.DropTargetDragEvent)
	 */
	public void dragOver(DropTargetDragEvent e)
	{
		try {
			DataFlavor[] flavors = e.getCurrentDataFlavors();
			if (flavors == null) return;
			if (flavors.length != 1) return;
			DataFlavor df = flavors[0];
			if (df == null) return;

			if (df.getPrimaryType().equals(WorkflowDesignerConstants.PTYPE)
				&& df.getSubType().equals(WorkflowDesignerConstants.STYPE_ACTIVITY_ITEM)) {
				Point location = e.getLocation();
				Dimension psize = this.getPreferredSize();
				if ((location.x >= psize.width) || (location.y >= psize.height)) {
					dropRect = null;
					repaint();
					return;
				}
				int x = location.x / GRID_SIZE;
				int y = location.y / GRID_SIZE;
					
				if (dropRect == null) {
					dropRect = new Rectangle(GRID_SIZE, GRID_SIZE);
				} 
				dropRect.x = x * GRID_SIZE;
				dropRect.y = y * GRID_SIZE;
				repaint();
			}
		} catch (Exception ex) {
		}
	}

	/**
	 * ドロップが行われたときに呼ばれる。
	 * 
	 * @param e DropTargetDropEvent
	 * @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent)
	 */
	public void drop(DropTargetDropEvent e)
	{
		e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);

		try {
			boolean invalid = false;
			Transferable t = e.getTransferable();
			if (t == null) invalid = true;
			DataFlavor[] flavors = t.getTransferDataFlavors();
			if (flavors == null) invalid = true;
			if (flavors.length != 1) invalid = true;
			DataFlavor df = flavors[0];
			if (df == null) invalid = true;
			if (df.getPrimaryType().equals(WorkflowDesignerConstants.PTYPE) == false
				|| df.getSubType().equals(WorkflowDesignerConstants.STYPE_ACTIVITY_ITEM) == false) {
				invalid = true;
			}

			if (invalid) {
				e.dropComplete(false);
				return;
			}

			String s = df.getHumanPresentableName();
			Point location = e.getLocation();
			dropActivity(s, location.x, location.y);

			e.dropComplete(true);
		} catch (Exception ex) {
			e.dropComplete(false);
		} finally {
			dropRect = null;
		}
	}

	/**
	 * ドロップアクションが切り替わったときに呼ばれる。
	 * 未使用
	 * 
	 * @param e DropTargetDragEvent
	 * @see java.awt.dnd.DropTargetListener#dropActionChanged(java.awt.dnd.DropTargetDragEvent)
	 */
	public void dropActionChanged(DropTargetDragEvent e) { /* nop */ }

	/**
	 * ビューコントローラの共通スーパークラス。
	 */
	class ProcessFlowViewController extends MouseAdapter
	implements MouseMotionListener
	{
		/**
		 * マウスドラッグが行われたときに呼ばれる。
		 * 
		 * @param e	マウスイベント
		 * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
		 */
		public void mouseDragged(MouseEvent e) { /* NOP */ }
		
		/**
		 * マウス移動が行われたときに呼ばれる。
		 * 
		 * @param e	マウスイベント
		 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
		 */
		public void mouseMoved(MouseEvent e) { /* NOP */ }
	}

	/**
	 * 編集モード用のビューコントローラ。
	 */
	public class PointerModeFlowViewController
	extends ProcessFlowViewController
	{
		/**
		 * マウスクリックを判定する。
		 *
		 * @param e マウスイベント
		 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
		 */
		public void mouseClicked(MouseEvent e)
		{
			if (e.getClickCount() >= 2) {
				inspectSelectedActivity(true);
			}
		}

		/**
		 * マウス押下を判定する。
		 *
		 * @param e マウスイベント
		 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
		 */
		public void mousePressed(MouseEvent e)
		{
			int mx = e.getX();
			int my = e.getY();
			int lx = mx / ProcessFlowView.GRID_SIZE;
			int ly = my / ProcessFlowView.GRID_SIZE;

			if (selectedRect == null) {
				selectedRect = new Rectangle(
							   ProcessFlowView.GRID_SIZE,
							   ProcessFlowView.GRID_SIZE
							   );
			} 
			selectedRect.x = lx * ProcessFlowView.GRID_SIZE;
			selectedRect.y = ly * ProcessFlowView.GRID_SIZE;

			setSelectedActivity(getActivity(mx, my));
		}

		/**
		 * マウスの押下解除を判定する。
		 *
		 * @param e マウスイベント
		 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
		 */
		public void mouseReleased(MouseEvent e)
		{
			int mx = e.getX();
			int my = e.getY();
			int lx = mx / ProcessFlowView.GRID_SIZE;
			int ly = my / ProcessFlowView.GRID_SIZE;

			if (selectedRect == null) {
				selectedRect = new Rectangle(
							   ProcessFlowView.GRID_SIZE,
							   ProcessFlowView.GRID_SIZE
							   );
			} 
			selectedRect.x = lx * ProcessFlowView.GRID_SIZE;
			selectedRect.y = ly * ProcessFlowView.GRID_SIZE;
			inspectSelectedActivity(false);
			repaint();
		}

		/**
		 * マウスの脱出を判定する。
		 *
		 * @param e マウスイベント
		 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
		 */
		public void mouseExited(MouseEvent e)
		{
			cursorRect = null;
			repaint();
		}

		/**
		 * マウスドラッグを判定する。
		 *
		 * @param	e		マウスイベント
		 * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
		 */
		public void mouseDragged(MouseEvent e)
		{
			if (selectedActivity == null) return;
			int mx = e.getX();
			int my = e.getY();
			int ax = selectedActivity.getLogicalX();
			int ay = selectedActivity.getLogicalY();
			Dimension psize = ProcessFlowView.this.getPreferredSize();

			if (mx < 0) mx = 0;
			if (my < 0) my = 0;
			if (mx >= psize.width) mx = psize.width - ProcessFlowView.GRID_SIZE;
			if (my >= psize.height) my = psize.height - ProcessFlowView.GRID_SIZE;
			int lx = mx / ProcessFlowView.GRID_SIZE;
			int ly = my / ProcessFlowView.GRID_SIZE;
			
			//移動場所にアクティビティがある場合は、移動しない。
			ActivityElement elem = getActivity(mx, my);
			if (elem != null) return;
			
			ConcurrentLayer layer = root.getContainLayer(selectedActivity);
			
			
			if (checkLocation(layer, selectedActivity, lx, ly) == false) return;
			
			if (ax != lx) selectedActivity.setLogicalX(lx);
			if (ay != ly) selectedActivity.setLogicalY(ly);

			if (selectedRect == null) {
				selectedRect = new Rectangle(
							   ProcessFlowView.GRID_SIZE,
							   ProcessFlowView.GRID_SIZE
							   );
			} 
			selectedRect.x = lx * ProcessFlowView.GRID_SIZE;
			selectedRect.y = ly * ProcessFlowView.GRID_SIZE;

			if (cursorRect == null) {
				cursorRect = new Rectangle(
								 ProcessFlowView.GRID_SIZE,
								 ProcessFlowView.GRID_SIZE
								 );
			} 
			cursorRect.x = lx * ProcessFlowView.GRID_SIZE;
			cursorRect.y = ly * ProcessFlowView.GRID_SIZE;
			
			repaint();
		}

		/**
		 * マウス移動を判定する。
		 *
		 * @param	e		マウスイベント
		 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
		 */
		public void mouseMoved(MouseEvent e)
		{
			int mx = e.getX();
			int my = e.getY();
			Dimension psize = ProcessFlowView.this.getPreferredSize();

			ResourceManager rm = WorkflowDesignerManager.getResourceManager();

			Element elem = getActivity(mx, my);
			if (elem == null) {
				setToolTipText(null);
			} else {
				setToolTipText(elem.getTooltipText(rm));
			} 

			if ((mx >= psize.width) || (my >= psize.height)) {
				cursorRect = null;
				repaint();
				return;
			}
			int lx = mx / ProcessFlowView.GRID_SIZE;
			int ly = my / ProcessFlowView.GRID_SIZE;
					
			if (cursorRect == null) {
				cursorRect = new Rectangle(
								 ProcessFlowView.GRID_SIZE,
								 ProcessFlowView.GRID_SIZE
								 );
			} 
			cursorRect.x = lx * ProcessFlowView.GRID_SIZE;
			cursorRect.y = ly * ProcessFlowView.GRID_SIZE;
			repaint();
		}
	}

	/**
	 * 接続モード時のビューコントローラ。
	 */
	public class ConnectionModeFlowViewController
	extends ProcessFlowViewController
	{
		/**
		 * マウスクリックを判定する。
		 *
		 * @param e マウスイベント
		 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
		 */
		public void mouseClicked(MouseEvent e)
		{
			if (sourceActivity == null) {
				sourceActivity = getActivity(e.getX(), e.getY());
				if (sourceActivity == null) {
					// nop
				} else if (sourceActivity.getType().equals(ActivityElement.END)) {
					sourceActivity = null;
				} else {
					if (e.isControlDown()) {
						setConnectingMode(CMODE_DISCONNECT);
					} else {
						setConnectingMode(CMODE_CONNECT);
					}
				}
			} else {
				ActivityElement dst
					= getActivity(e.getX(), e.getY());
				if (dst != null) {
					dst.getID();
					if (connectingMode == CMODE_DISCONNECT) {
						removeTransition(sourceActivity, dst);
					} else {
						addTransition(sourceActivity, dst);
					}
				}
				sourceActivity = null;
			}
			repaint();
		}

		/**
		 * マウスの脱出を判定する。
		 *
		 * @param e マウスイベント
		 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
		 */
		public void mouseExited(MouseEvent e)
		{
			cursorRect = null;
			dropRect = null;
			repaint();
		}

		/**
		 * マウス移動を判定する。
		 *
		 * @param	e		マウスイベント
		 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
		 */
		public void mouseMoved(MouseEvent e)
		{
			int mx = e.getX();
			int my = e.getY();
			Dimension psize = ProcessFlowView.this.getPreferredSize();

			if ((mx >= psize.width) || (my >= psize.height)) {
				cursorRect = null;
				repaint();
				return;
			}
			int lx = mx / ProcessFlowView.GRID_SIZE;
			int ly = my / ProcessFlowView.GRID_SIZE;
					
			if (cursorRect == null) {
				cursorRect = new Rectangle(
								 ProcessFlowView.GRID_SIZE,
								 ProcessFlowView.GRID_SIZE
								 );
			} 
			cursorRect.x = lx * ProcessFlowView.GRID_SIZE;
			cursorRect.y = ly * ProcessFlowView.GRID_SIZE;
			repaint();
		}
	}

	/**
	 * シミュレートモード時のビューコントローラ。
	 */
	public class SimulationModeFlowViewController
	extends ProcessFlowViewController
	{
		/**
		 * マウスクリックを判定する。
		 *
		 * @param e マウスイベント
		 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
		 */
		public void mouseClicked(MouseEvent e)
		{
			ResourceManager rm = WorkflowDesignerManager.getResourceManager();

			if (e.getClickCount() >= 2) {
				int mx = e.getX();
				int my = e.getY();
				ActivityElement act = getActivity(mx, my);
				if (act == null) return;
				
				SimulatableActivityWrapper simact = null;
				for (SimulatableActivityWrapper sa : simulatedActivities) {
					if (sa.getActivity() == act) {
						simact = sa;
						break;
					}
				}
				if (simact != null) {
					if (waitActivities.containsKey(simact)) {
						SimulatableActivityWrapper top
							= (SimulatableActivityWrapper)waitActivities.get(simact);
						if (checkHasBlockElement(top)) {
							DialogUtilities.showWarning(rm.getResource("message.warn.0201"));
							return;
						}
						removeBlock(top, simact);
					}
					PackageElement pkg = WorkflowDesignerManager.getMainFrame().getPackage();
					List<ConditionalVariable> vars = makeConditionalVariables(pkg.getVariables());
					if (act == getStartActivity()) {
						simulator.start(ProcessFlowView.this, vars, proc, simact);
					} else {
						simulator.simulate(simact);
					}
				}
			}
		}

		/**
		 * プロセス変数から条件変数を作成する。
		 * 
		 * @param vars	プロセス変数一覧
		 * @return	条件変数一覧
		 */			
		private List<ConditionalVariable> makeConditionalVariables(List<VariableElement> vars)
		{
			List<ConditionalVariable> result = new ArrayList<ConditionalVariable>();
			for (VariableElement var : vars) {
				ConditionalVariable cvar = new ConditionalVariable(var);
				result.add(cvar);
			}
			return result;
		}
	}
}

