/* * Copyright (C) 2012 Jason Gedge <http://www.gedge.ca> * * This file is part of the OpGraph project. * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ /** * */ package ca.gedge.opgraph.app.edits.graph; import java.awt.Dimension; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.JComponent; import javax.swing.undo.CompoundEdit; import ca.gedge.opgraph.OpGraph; import ca.gedge.opgraph.OpNode; import ca.gedge.opgraph.app.components.canvas.CanvasNode; import ca.gedge.opgraph.app.extensions.NodeMetadata; /** * An edit which automatically lays out nodes in a graph. */ public class AutoLayoutEdit extends CompoundEdit { /** * Constructs an edit that automatically lays out nodes in a graph. * * @param graph the graph * * @throws IllegalArgumentException if at least one node in the graph does * not have a {@link JComponent} extension */ public AutoLayoutEdit(OpGraph graph) { // First make sure every node has a JComponent extension for(OpNode node : graph.getVertices()) { if(node.getExtension(JComponent.class) == null) throw new IllegalArgumentException("Not all npdes have JComponent extension"); } // Perform automatic layout final List<OpNode> nodes = new ArrayList<OpNode>(graph.getVertices()); int x = 15; for(int level = 0; !nodes.isEmpty(); ++level) { int maxWidth = 0; int y = 15; final Iterator<OpNode> iter = nodes.iterator(); while(iter.hasNext()) { final OpNode v = iter.next(); // Skip if node not at this level, otherwise remove it // because it shouldn't be processed at higher levels if(graph.getLevel(v) != level) continue; iter.remove(); // Place those nodes final JComponent node = v.getExtension(JComponent.class); final Dimension pref = node.getPreferredSize(); // If metadata null, add a metadata extension. NodeMetadata meta = v.getExtension(NodeMetadata.class); if(meta == null) { meta = new NodeMetadata(x, y); v.putExtension(NodeMetadata.class, meta); } // Post the undoable edit final ArrayList<OpNode> nodeToMove = new ArrayList<OpNode>(); nodeToMove.add(v); final int deltaX = x - meta.getX(); final int deltaY = y - meta.getY(); if(deltaX != 0 || deltaY != 0) addEdit(new MoveNodesEdit(nodeToMove, deltaX, deltaY)); // Move downwards, and extend the max width to this node, if it // is bigger than any previous node maxWidth = Math.max(maxWidth, pref.width); y += pref.height + 15; } x += maxWidth + 50; } super.end(); } // // CompoundEdit // @Override public String getUndoPresentationName() { return "Undo " + getPresentationName(); } @Override public String getRedoPresentationName() { return "Redo " + getPresentationName(); } @Override public String getPresentationName() { return "Auto Layout"; } }