S-JIS[2013-03-16] 変更履歴

Eclipse GEF 自動レイアウト

Eclipseプラグイン開発GEFの図形の自動レイアウトについて。


概要

GEFでは、図形の配置を自動的に整列させることが出来る。

あんまり思った通りの並び順になってくれないけど(苦笑)
横向きに並べたい場合に、上下(行)が入れ替わってしまったりするのがなぁ。(初期位置を指定できないので、入れ替わるというのはこちらの主観なんだけど)


選択された図形を自動整列させる例。

まず、自動整列を実行するアクションを定義する。

AutoLayoutAction.java

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.graph.DirectedGraph;
import org.eclipse.draw2d.graph.DirectedGraphLayout;
import org.eclipse.draw2d.graph.Edge;
import org.eclipse.draw2d.graph.EdgeList;
import org.eclipse.draw2d.graph.Node;
import org.eclipse.draw2d.graph.NodeList;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.ui.actions.SelectionAction;
import org.eclipse.ui.IWorkbenchPart;
public class AutoLayoutAction extends SelectionAction {

	public static final String ID = "MY_AUTO_LAYOUT";
	public AutoLayoutAction(IWorkbenchPart part) {
		super(part);
	}
	@Override
	protected void init() {
		super.init();
		setText("auto layout");
		setToolTipText("auto layout");
		setId(ID);
	}
	@Override
	protected boolean calculateEnabled() {
		@SuppressWarnings("unchecked")
		List<Object> list = getSelectedObjects();
		int count = 0;
		for (Object obj : list) {
			if (obj instanceof NodeElementEditPart) {
				if (++count >= 2) { // 要素が2個以上選択されている場合だけ実行可能
					return true;
				}
			}
		}
		return false;
	}
	@Override
	public void run() {
		NodeList nodeList = new NodeList();
		EdgeList edgeList = new EdgeList();
		collect(nodeList, edgeList);
		calculateLayout(nodeList, edgeList);
		layout(nodeList);
	}
	// 選択された要素から、Node(頂点)とEdge(辺)を収集する
	@SuppressWarnings("unchecked")
	private void collect(NodeList nodeList, EdgeList edgeList) {
		Map<NodeElement, Node> map = new HashMap<NodeElement, Node>();
		{
			List<Object> list = getSelectedObjects();
			for (Object obj : list) {
				if (obj instanceof NodeElementEditPart) {
					NodeElementEditPart part = (NodeElementEditPart) obj;
					NodeElement model = part.getModel();
					Node node = new Node(part);
					map.put(model, node);

					IFigure figure = part.getFigure();
					Dimension size = figure.getSize();
					node.setSize(size);
					// 初期位置を与えても考慮されない
					// node.x = model.getX();
					// node.y = model.getY();
				}
			}
		}

		Set<Connection> set = new HashSet<Connection>();
		for (Entry<NodeElement, Node> entry : map.entrySet()) {
			NodeElement model = entry.getKey();
			Node node = entry.getValue();
			nodeList.add(node);
			for (Connection c : model.getOutgoings()) {
				Node target = map.get(c.getTarget());
				if (target != null && !set.contains(c)) {
					Edge edge = new Edge(node, target);
					edgeList.add(edge);
					set.add(c);
				}
			}
			for (Connection c : model.getIncomings()) {
				Node source = map.get(c.getSource());
				if (source != null && !set.contains(c)) {
					Edge edge = new Edge(source, node);
					edgeList.add(edge);
					set.add(c);
				}
			}
		}
	}
	// 自動レイアウトを実施する
	private void calculateLayout(NodeList nodeList, EdgeList edgeList) {
		DirectedGraph graph = new DirectedGraph();
		graph.setDirection(PositionConstants.EAST); // 左から右の方向へ配置する
		graph.edges = edgeList;
		graph.nodes = nodeList;
		graph.setDefaultPadding(new Insets(16)); // Node間の間隔
		DirectedGraphLayout layout = new DirectedGraphLayout();
		layout.visit(graph); //これにより、nodeListに座標が設定される
	}
	// 算出された位置へ図形を移動させる
	private void layout(NodeList nodeList) {
		CompoundCommand command = new CompoundCommand();

		for (Object obj : nodeList) {
			Node node = (Node) obj;
			NodeElementEditPart part = (NodeElementEditPart) node.data;
			NodeElement model = part.getModel();
			MoveElementCommand c = new MoveElementCommand(model, node.x, node.y);
			command.add(c);
		}

		getCommandStack().execute(command);
	}
}

GraphicalEditorクラス:

public class MyEditor extends GraphicalEditorWithFlyoutPalette
	@SuppressWarnings("unchecked")
	@Override
	protected void createActions() {
		super.createActions();
		ActionRegistry registry = getActionRegistry();
〜
		IAction action = new AutoLayoutAction(this);
		registry.registerAction(action);
		getSelectionActions().add(action.getId());
	}

ContextMenuProviderクラス:

public class MyContextMenuProvider extends ContextMenuProvider {
	@Override
	public void buildContextMenu(IMenuManager menu) {
		GEFActionConstants.addStandardActionGroups(menu);
〜
		menu.appendToGroup(GEFActionConstants.GROUP_EDIT, registry.getAction(AutoLayoutAction.ID));
	}

コンテキストメニューにアクションを追加してみた。
(グループをどこにすればいいか迷う^^;)


GEFへ戻る / Eclipseプラグインへ戻る / Eclipseへ戻る / 技術メモへ戻る
メールの送信先:ひしだま