| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.oodt.cas.workflow.gui.perspective.view.impl; |
| |
| //JDK imports |
| import java.awt.BorderLayout; |
| import java.awt.Color; |
| import java.awt.Cursor; |
| import java.awt.Graphics2D; |
| import java.awt.MenuItem; |
| import java.awt.Point; |
| import java.awt.PopupMenu; |
| import java.awt.Rectangle; |
| import java.awt.Toolkit; |
| import java.awt.datatransfer.DataFlavor; |
| import java.awt.datatransfer.Transferable; |
| import java.awt.datatransfer.UnsupportedFlavorException; |
| import java.awt.dnd.DnDConstants; |
| import java.awt.dnd.DragGestureEvent; |
| import java.awt.dnd.DragGestureListener; |
| import java.awt.dnd.DragSource; |
| import java.awt.dnd.DragSourceDragEvent; |
| import java.awt.dnd.DragSourceDropEvent; |
| import java.awt.dnd.DragSourceEvent; |
| import java.awt.dnd.DragSourceListener; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.MouseEvent; |
| import java.awt.event.MouseListener; |
| import java.awt.event.MouseMotionListener; |
| import java.awt.event.MouseWheelListener; |
| import java.awt.geom.Point2D; |
| import java.awt.geom.Rectangle2D; |
| import java.awt.image.BufferedImage; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Vector; |
| import java.util.Map.Entry; |
| import javax.swing.JScrollPane; |
| import javax.swing.SwingConstants; |
| import javax.swing.border.LineBorder; |
| import javax.swing.tree.DefaultMutableTreeNode; |
| |
| //JGraph imports |
| import org.jgraph.JGraph; |
| import org.jgraph.graph.AttributeMap; |
| import org.jgraph.graph.DefaultGraphCell; |
| import org.jgraph.graph.DefaultEdge; |
| import org.jgraph.graph.GraphConstants; |
| import com.jgraph.layout.JGraphFacade; |
| import com.jgraph.layout.hierarchical.JGraphHierarchicalLayout; |
| |
| //Jung imports |
| import edu.uci.ics.jung.graph.DirectedSparseGraph; |
| import edu.uci.ics.jung.graph.ObservableGraph; |
| |
| //OODT imports |
| import org.apache.oodt.cas.workflow.gui.model.ModelGraph; |
| import org.apache.oodt.cas.workflow.gui.model.ModelNode; |
| import org.apache.oodt.cas.workflow.gui.perspective.view.View; |
| import org.apache.oodt.cas.workflow.gui.perspective.view.ViewChange; |
| import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState; |
| import org.apache.oodt.cas.workflow.gui.util.GuiUtils; |
| import org.apache.oodt.cas.workflow.gui.util.IconLoader; |
| import org.apache.oodt.cas.workflow.gui.util.Line; |
| |
| /** |
| * |
| * This is where the money happens. The Graph visualization of OODT CAS |
| * workflows is displayed via this view, which magically integrates JGraph, |
| * Jung, and OODT. |
| * |
| * @author bfoster |
| * @author mattmann |
| * |
| */ |
| public class GraphView extends DefaultTreeView { |
| |
| private static final long serialVersionUID = 5935578385254884387L; |
| |
| private JungJGraphModelAdapter m_jgAdapter; |
| private JGraph jgraph; |
| private ObservableGraph<ModelNode, IdentifiableEdge> directedGraph; |
| |
| private MyGraphListener myGraphListener; |
| |
| private static final String VIEW_REF_WORKFLOW = "View Referrenced"; |
| private static final String ACTIONS_POP_MENU_NAME = "Actions"; |
| private static final String NEW_SUB_POP_MENU_NAME = "New"; |
| private static final String NEW_TASK_ITEM_NAME = "Task"; |
| private static final String NEW_CONDITION_ITEM_NAME = "Condition"; |
| private static final String NEW_PARALLEL_ITEM_NAME = "Parallel"; |
| private static final String NEW_SEQUENTIAL_ITEM_NAME = "Sequential"; |
| private static final String EDGES_SUB_POP_MENU_NAME = "Edges"; |
| private static final String TASK_LEVEL = "Task Level"; |
| private static final String WORKFLOW_LEVEL = "Workflow Level"; |
| private static final String DELETE_ITEM_NAME = "Delete"; |
| private static final String FORMAT_ITEM_NAME = "Format"; |
| private static final String ORDER_SUB_POP_MENU_NAME = "Order"; |
| private static final String TO_FRONT_ITEM_NAME = "Move To Front"; |
| private static final String TO_BACK_ITEM_NAME = "Move To Back"; |
| private static final String FORWARD_ITEM_NAME = "Move Forward"; |
| private static final String BACKWARDS_ITEM_NAME = "Move Backwards"; |
| |
| private HashMap<String, Pair> edgeMap; |
| |
| private static final String SCALE = "GraphView/scale"; |
| private static final String EDGE_DISPLAY_MODE = "GraphView/EdgeDisplay/Mode"; |
| private static final String TASK_MODE = "Task"; |
| private static final String WORKFLOW_MODE = "Workflow"; |
| |
| private static boolean scrollSelectedToVisible = false; |
| |
| public GraphView(String name) { |
| super(name); |
| } |
| |
| @Override |
| public void refreshView(final ViewState state) { |
| this.removeAll(); |
| this.myGraphListener = new MyGraphListener(state); |
| |
| Rectangle visible = null; |
| if (jgraph != null) |
| visible = jgraph.getVisibleRect(); |
| |
| Cursor cursor = null; |
| if (jgraph != null) |
| cursor = jgraph.getCursor(); |
| |
| this.edgeMap = new HashMap<String, Pair>(); |
| |
| directedGraph = new ObservableGraph<ModelNode, IdentifiableEdge>( |
| new DirectedSparseGraph<ModelNode, IdentifiableEdge>()); |
| m_jgAdapter = new JungJGraphModelAdapter(directedGraph); |
| |
| jgraph = new JGraph(m_jgAdapter); |
| for (MouseListener ml : jgraph.getMouseListeners()) |
| jgraph.removeMouseListener(ml); |
| for (MouseMotionListener ml : jgraph.getMouseMotionListeners()) |
| jgraph.removeMouseMotionListener(ml); |
| for (MouseWheelListener ml : jgraph.getMouseWheelListeners()) |
| jgraph.removeMouseWheelListener(ml); |
| jgraph.setBackground(Color.white); |
| jgraph.setAntiAliased(true); |
| jgraph.setMoveable(false); |
| String scale = state.getFirstPropertyValue(SCALE); |
| if (scale == null) |
| state.setProperty(SCALE, scale = "1.0"); |
| jgraph.setScale(Double.parseDouble(scale)); |
| |
| DragSource dragSource = DragSource.getDefaultDragSource(); |
| dragSource.createDefaultDragGestureRecognizer(this.jgraph, |
| DnDConstants.ACTION_MOVE, new DragGestureListener() { |
| |
| DefaultGraphCell moveCell = null; |
| ModelGraph moveGraph = null; |
| |
| public void dragGestureRecognized(final DragGestureEvent dge) { |
| if (state.getMode() == View.Mode.MOVE |
| || state.getMode() == View.Mode.EDIT) { |
| Object moveOverCell = jgraph.getFirstCellForLocation(dge |
| .getDragOrigin().getX(), dge.getDragOrigin().getY()); |
| if (moveOverCell != null) { |
| if (moveOverCell instanceof DefaultEdge) { |
| moveCell = null; |
| } else if (moveOverCell instanceof DefaultGraphCell) { |
| moveCell = (DefaultGraphCell) moveOverCell; |
| moveGraph = GuiUtils.find(state.getGraphs(), |
| ((ModelNode) ((DefaultGraphCell) moveCell) |
| .getUserObject()).getId()); |
| |
| if (state.getMode() == View.Mode.MOVE) |
| moveCell = GraphView.this.m_jgAdapter |
| .getVertexCell((moveGraph = moveGraph.getRootParent()) |
| .getModel()); |
| else if (GuiUtils.isDummyNode(moveGraph.getModel())) |
| moveCell = GraphView.this.m_jgAdapter |
| .getVertexCell((moveGraph = moveGraph.getParent()) |
| .getModel()); |
| else if (moveGraph.getModel().isRef()) { |
| while (moveGraph.getParent() != null |
| && moveGraph.getParent().getModel().isRef()) |
| moveGraph = moveGraph.getParent(); |
| moveCell = GraphView.this.m_jgAdapter |
| .getVertexCell(moveGraph.getModel()); |
| } |
| final double scale = Double.parseDouble(state |
| .getFirstPropertyValue(SCALE)); |
| final Rectangle2D bounds = (Rectangle2D) GraphView.this.jgraph |
| .getAttributes(moveCell).get(GraphConstants.BOUNDS); |
| Point offset = new Point( |
| (int) (bounds.getX() * scale - dge.getDragOrigin().getX()), |
| (int) (bounds.getY() * scale - dge.getDragOrigin().getY())); |
| BufferedImage image = new BufferedImage((int) (bounds |
| .getWidth() * scale), (int) (bounds.getHeight() * scale), |
| BufferedImage.TYPE_INT_ARGB); |
| Graphics2D g2d = image.createGraphics(); |
| g2d.setColor(Color.black); |
| g2d.drawRect((int) (2 * scale), (int) (2 * scale), |
| (int) ((bounds.getWidth() - 4) * scale), |
| (int) ((bounds.getHeight() - 4) * scale)); |
| dge.startDrag(GraphView.this.getCursor(), image, offset, |
| new Transferable() { |
| |
| public Object getTransferData(DataFlavor flavor) |
| throws UnsupportedFlavorException, IOException { |
| if (flavor.getHumanPresentableName().equals( |
| DefaultGraphCell.class.getSimpleName())) |
| return this; |
| else |
| throw new UnsupportedFlavorException(flavor); |
| } |
| |
| public DataFlavor[] getTransferDataFlavors() { |
| return new DataFlavor[] { new DataFlavor( |
| DefaultGraphCell.class, DefaultGraphCell.class |
| .getSimpleName()) }; |
| } |
| |
| public boolean isDataFlavorSupported(DataFlavor flavor) { |
| if (flavor.getHumanPresentableName().equals( |
| DefaultGraphCell.class.getSimpleName())) |
| return true; |
| else |
| return false; |
| } |
| |
| }, new DragSourceListener() { |
| |
| private ModelGraph mouseOverGraph; |
| private DefaultGraphCell mouseOverCell; |
| |
| public void dragDropEnd(DragSourceDropEvent dsde) { |
| System.out.println("DRAG END!!!!"); |
| if (moveCell == null) |
| return; |
| Point dropPoint = new Point(dsde.getX() |
| - jgraph.getLocationOnScreen().x, dsde.getY() |
| - jgraph.getLocationOnScreen().y); |
| DefaultGraphCell endCell = (DefaultGraphCell) GraphView.this.jgraph |
| .getSelectionCell(); |
| if (endCell != null) { |
| ModelGraph endGraph = GuiUtils.find( |
| state.getGraphs(), |
| ((ModelNode) endCell.getUserObject()).getId()); |
| if (!endGraph.getModel().isParentType() |
| || GuiUtils.isSubGraph(moveGraph, endGraph)) |
| return; |
| if (moveGraph.getParent() == null) |
| state.removeGraph(moveGraph); |
| else |
| GuiUtils.removeNode(state.getGraphs(), |
| moveGraph.getModel()); |
| GraphView.this.removeShift(state, moveGraph); |
| GuiUtils.addChild(state.getGraphs(), endGraph |
| .getModel().getId(), moveGraph); |
| GraphView.this.notifyListeners(); |
| } else { |
| if (moveGraph.getParent() != null) { |
| GuiUtils.removeNode(state.getGraphs(), |
| moveGraph.getModel()); |
| state.addGraph(moveGraph); |
| } |
| Point shiftPoint = new Point( |
| (int) ((dropPoint.x - (dge.getDragOrigin().x - (bounds |
| .getX() * scale))) / scale), |
| (int) ((dropPoint.y - (dge.getDragOrigin().y - (bounds |
| .getY() * scale))) / scale)); |
| GraphView.this.setShift(state, moveGraph, |
| shiftPoint); |
| GraphView.this.notifyListeners(); |
| } |
| } |
| |
| public void dragEnter(DragSourceDragEvent dsde) { |
| mouseOverCell = (DefaultGraphCell) GraphView.this.jgraph |
| .getFirstCellForLocation( |
| dsde.getX() - jgraph.getLocationOnScreen().x, |
| dsde.getY() - jgraph.getLocationOnScreen().y); |
| mouseOverGraph = GuiUtils.find(state.getGraphs(), |
| ((ModelNode) mouseOverCell.getUserObject()) |
| .getId()); |
| } |
| |
| public void dragExit(DragSourceEvent dse) { |
| System.out.println("DRAG EXIT!!!!"); |
| } |
| |
| public void dragOver(DragSourceDragEvent dsde) { |
| if (state.getMode().equals(Mode.EDIT)) { |
| if (mouseOverCell != null) { |
| Rectangle2D currentBounds = (Rectangle2D) mouseOverCell |
| .getAttributes().get(GraphConstants.BOUNDS); |
| Point currentPoint = new Point(dsde.getX() |
| - jgraph.getLocationOnScreen().x, dsde.getY() |
| - jgraph.getLocationOnScreen().y); |
| if (currentBounds.contains(currentPoint)) { |
| for (ModelGraph child : mouseOverGraph |
| .getChildren()) { |
| DefaultGraphCell mouseOverCellLoc = GraphView.this.m_jgAdapter |
| .getVertexCell(child.getModel()); |
| currentBounds = (Rectangle2D) mouseOverCellLoc |
| .getAttributes().get( |
| GraphConstants.BOUNDS); |
| if (currentBounds.contains(currentPoint)) { |
| mouseOverCell = mouseOverCellLoc; |
| mouseOverGraph = child; |
| break; |
| } |
| } |
| } else { |
| if (mouseOverGraph.getParent() != null |
| && (!mouseOverGraph.isCondition() || (mouseOverGraph |
| .isCondition() && mouseOverGraph |
| .getParent().isCondition()))) { |
| mouseOverCell = GraphView.this.m_jgAdapter |
| .getVertexCell((mouseOverGraph = mouseOverGraph |
| .getParent()).getModel()); |
| currentBounds = (Rectangle2D) mouseOverCell |
| .getAttributes().get( |
| GraphConstants.BOUNDS); |
| } else { |
| mouseOverCell = null; |
| mouseOverGraph = null; |
| } |
| } |
| } else { |
| mouseOverCell = (DefaultGraphCell) GraphView.this.jgraph |
| .getFirstCellForLocation( |
| dsde.getX() |
| - jgraph.getLocationOnScreen().x, |
| dsde.getY() |
| - jgraph.getLocationOnScreen().y); |
| if (mouseOverCell != null) |
| mouseOverGraph = GuiUtils.find(state |
| .getGraphs(), ((ModelNode) mouseOverCell |
| .getUserObject()).getId()); |
| else |
| mouseOverGraph = null; |
| } |
| if (mouseOverGraph != null) { |
| if (GuiUtils.isDummyNode(mouseOverGraph |
| .getModel())) { |
| mouseOverGraph = mouseOverGraph.getParent(); |
| } else { |
| while (mouseOverGraph != null |
| && mouseOverGraph.getModel().isRef()) |
| mouseOverGraph = mouseOverGraph.getParent(); |
| } |
| if (mouseOverGraph != null) |
| mouseOverCell = GraphView.this.m_jgAdapter |
| .getVertexCell(mouseOverGraph.getModel()); |
| else |
| mouseOverCell = null; |
| } |
| GraphView.this.jgraph |
| .setSelectionCells(new Object[] { mouseOverCell }); |
| } |
| } |
| |
| public void dropActionChanged(DragSourceDragEvent dsde) { |
| System.out.println("DRAG CHANGE!!!!"); |
| } |
| |
| }); |
| } |
| } |
| } |
| } |
| |
| }); |
| |
| List<Line> lines = GuiUtils.findLines(state.getGraphs()); |
| for (Line line : lines) { |
| if (!this.directedGraph.containsVertex(line.getFromModel())) |
| this.directedGraph.addVertex(line.getFromModel()); |
| |
| if (line.getToModel() != null) { |
| if (!this.directedGraph.containsVertex(line.getToModel())) |
| this.directedGraph.addVertex(line.getToModel()); |
| IdentifiableEdge edge = new IdentifiableEdge(line.getFromModel(), line.getToModel()); |
| directedGraph.addEdge(edge, line.getFromModel(), line.getToModel()); |
| this.edgeMap.put(edge.id, new Pair(line.getFromModel() != null ? line |
| .getFromModel().getId() : null, line.getToModel().getId())); |
| } |
| } |
| |
| JGraphFacade facade = new JGraphFacade(jgraph); |
| facade.setIgnoresUnconnectedCells(false); |
| JGraphHierarchicalLayout layout = new JGraphHierarchicalLayout(); |
| layout.setOrientation(SwingConstants.WEST); |
| layout.setIntraCellSpacing(70.0); |
| layout.setLayoutFromSinks(false); |
| layout.run(facade); |
| Map nested = facade.createNestedMap(true, true); |
| if (nested != null) { |
| this.hideDummyNodes(nested); |
| this.addGroups(state.getGraphs(), nested, state); |
| this.ensureNoOverlap(nested, state); |
| nested = this.shiftMap(nested, state); |
| jgraph.getGraphLayoutCache().edit(nested); |
| } |
| |
| String edgeDisplayMode = state.getFirstPropertyValue(EDGE_DISPLAY_MODE); |
| if (edgeDisplayMode == null) |
| state.setProperty(EDGE_DISPLAY_MODE, edgeDisplayMode = WORKFLOW_MODE); |
| if (edgeDisplayMode.equals(WORKFLOW_MODE)) { |
| this.edgeMap = new HashMap<String, Pair>(); |
| removeAllEdges(this.directedGraph); |
| lines = GuiUtils.findSequentialLines(state.getGraphs()); |
| for (Line line : lines) { |
| IdentifiableEdge edge = new IdentifiableEdge(line.getFromModel(), line.getToModel()); |
| directedGraph.addEdge(edge, line.getFromModel(), line.getToModel()); |
| this.edgeMap.put(edge.id, new Pair(line.getFromModel() != null ? line |
| .getFromModel().getId() : null, line.getToModel().getId())); |
| } |
| } |
| |
| if (state.getSelected() != null) { |
| ModelGraph graph = GuiUtils.find(state.getGraphs(), state.getSelected() |
| .getModel().getId()); |
| if (graph != null) { |
| DefaultGraphCell cell = this.m_jgAdapter |
| .getVertexCell(graph.getModel()); |
| if (cell != null) |
| this.jgraph.setSelectionCells(new Object[] { cell }); |
| else |
| this.jgraph.setSelectionCells(new Object[] {}); |
| } else |
| this.jgraph.setSelectionCells(new Object[] {}); |
| } else { |
| this.jgraph.setSelectionCells(new Object[] {}); |
| } |
| |
| jgraph.addMouseListener(this.myGraphListener); |
| |
| this.setLayout(new BorderLayout()); |
| this.add(new JScrollPane(jgraph, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, |
| JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS), BorderLayout.CENTER); |
| |
| if (scrollSelectedToVisible && state.getSelected() != null) { |
| this.jgraph.scrollCellToVisible(GraphView.this.m_jgAdapter |
| .getVertexCell(state.getSelected().getModel())); |
| scrollSelectedToVisible = false; |
| } else if (visible != null) { |
| this.jgraph.scrollRectToVisible(visible); |
| } |
| |
| if (cursor != null) |
| this.jgraph.setCursor(cursor); |
| |
| this.revalidate(); |
| } |
| |
| private void hideDummyNodes(Map nested) { |
| for (Object cell : nested.keySet()) { |
| if (cell instanceof DefaultEdge) { |
| // do nothing |
| } else if (cell instanceof DefaultGraphCell) { |
| if (GuiUtils.isDummyNode((ModelNode) ((DefaultGraphCell) cell) |
| .getUserObject())) { |
| ((Map<Object, Object>) nested.get(cell)).put("opaque", false); |
| ((Map<Object, Object>) nested.get(cell)).put("backgroundColor", |
| Color.white); |
| } |
| } |
| } |
| } |
| |
| private Map shiftMap(Map nested, ViewState state) { |
| Map shiftedNested = new Hashtable(nested); |
| for (Object obj : shiftedNested.entrySet()) { |
| Entry entry = (Entry) obj; |
| if (entry.getKey() instanceof DefaultEdge) { |
| ArrayList<Point2D.Double> points = (ArrayList<Point2D.Double>) ((Map<Object, Object>) entry |
| .getValue()).get("points"); |
| ArrayList<Point2D.Double> newPoints = new ArrayList<Point2D.Double>(); |
| Point shift = this.getShift(state, (DefaultGraphCell) entry.getKey(), |
| nested); |
| for (Point2D.Double point : points) |
| newPoints |
| .add(new Point2D.Double(point.x + shift.x, point.y + shift.y)); |
| ((Map<Object, Object>) entry.getValue()).put("points", newPoints); |
| } else if (entry.getKey() instanceof DefaultGraphCell) { |
| DefaultGraphCell cell = (DefaultGraphCell) entry.getKey(); |
| Rectangle2D bounds = (Rectangle2D) ((Map<Object, Object>) entry |
| .getValue()).get("bounds"); |
| Point shift = this.getShift(state, cell, nested); |
| AttributeMap.SerializableRectangle2D newBounds = new AttributeMap.SerializableRectangle2D( |
| bounds.getX() + shift.x, bounds.getY() + shift.y, |
| bounds.getWidth(), bounds.getHeight()); |
| Map<Object, Object> newMap = new Hashtable<Object, Object>( |
| (Map<Object, Object>) entry.getValue()); |
| newMap.put("bounds", newBounds); |
| shiftedNested.put(cell, newMap); |
| } |
| } |
| return shiftedNested; |
| } |
| |
| private void ensureNoOverlap(Map nested, ViewState state) { |
| boolean changed; |
| do { |
| changed = false; |
| for (int i = 0; i < state.getGraphs().size(); i++) { |
| ModelGraph currentGraph = state.getGraphs().get(i); |
| if (this.ensureNoInternalOverlap(currentGraph, nested)) |
| changed = true; |
| DefaultGraphCell currentCell = this.m_jgAdapter |
| .getVertexCell(currentGraph.getModel()); |
| Rectangle2D currentBounds = (Rectangle2D) ((Map) nested |
| .get(currentCell)).get(GraphConstants.BOUNDS); |
| Point currentShift = this.getShift(state, currentCell, nested); |
| Rectangle currentShiftBounds = new Rectangle( |
| (int) (currentBounds.getX() + currentShift.getX()), |
| (int) (currentBounds.getY() + currentShift.getY()), |
| (int) currentBounds.getWidth(), (int) currentBounds.getHeight()); |
| for (int j = 0; j < state.getGraphs().size(); j++) { |
| if (i == j) |
| continue; |
| ModelGraph graph = state.getGraphs().get(j); |
| DefaultGraphCell cell = this.m_jgAdapter.getVertexCell(graph |
| .getModel()); |
| Rectangle2D bounds = (Rectangle2D) ((Map) nested.get(cell)) |
| .get(GraphConstants.BOUNDS); |
| Point shift = this.getShift(state, cell, nested); |
| Rectangle shiftBounds = new Rectangle( |
| (int) (bounds.getX() + shift.getX()), |
| (int) (bounds.getY() + shift.getY()), (int) bounds.getWidth(), |
| (int) bounds.getHeight()); |
| if (currentShiftBounds.intersects(shiftBounds)) { |
| changed = true; |
| if (currentShiftBounds.getY() < shiftBounds.getY()) { |
| Rectangle intersection = currentShiftBounds |
| .intersection(shiftBounds); |
| if (currentShiftBounds.getY() + currentShiftBounds.getHeight() > shiftBounds |
| .getY() + shiftBounds.getHeight()) { |
| this.setShift(state, graph, |
| new Point((int) (currentShiftBounds.getX() |
| + currentShiftBounds.getWidth() + 20.0), |
| (int) shiftBounds.getY())); |
| } else { |
| this.setShift( |
| state, |
| graph, |
| new Point((int) shiftBounds.getX(), (int) (shiftBounds |
| .getY() + intersection.getHeight() + 20.0))); |
| } |
| } else { |
| Rectangle intersection = shiftBounds |
| .intersection(currentShiftBounds); |
| if (shiftBounds.getY() + shiftBounds.getHeight() > currentShiftBounds |
| .getY() + currentShiftBounds.getHeight()) { |
| this.setShift( |
| state, |
| currentGraph, |
| new Point((int) (shiftBounds.getX() |
| + shiftBounds.getWidth() + 20.0), |
| (int) currentShiftBounds.getY())); |
| } else { |
| this.setShift( |
| state, |
| currentGraph, |
| new Point((int) currentShiftBounds.getX(), |
| (int) (currentShiftBounds.getY() |
| + intersection.getHeight() + 20.0))); |
| } |
| |
| currentShift = this.getShift(state, currentCell, nested); |
| currentShiftBounds = new Rectangle( |
| (int) (currentBounds.getX() + currentShift.getX()), |
| (int) (currentBounds.getY() + currentShift.getY()), |
| (int) currentBounds.getWidth(), |
| (int) currentBounds.getHeight()); |
| } |
| } |
| } |
| } |
| } while (changed); |
| } |
| |
| private boolean ensureNoInternalOverlap(final ModelGraph graph, |
| final Map nested) { |
| boolean changed = false; |
| if (graph.getChildren().size() > 1) { |
| List<ModelGraph> sortedChildren = new Vector<ModelGraph>( |
| graph.getChildren()); |
| Collections.sort(sortedChildren, new Comparator<ModelGraph>() { |
| |
| public int compare(ModelGraph o1, ModelGraph o2) { |
| DefaultGraphCell child1Cell = GraphView.this.m_jgAdapter |
| .getVertexCell(o1.getModel()); |
| DefaultGraphCell child2Cell = GraphView.this.m_jgAdapter |
| .getVertexCell(o2.getModel()); |
| Rectangle2D child1Bounds = (Rectangle2D) ((Map) nested |
| .get(child1Cell)).get(GraphConstants.BOUNDS); |
| Rectangle2D child2Bounds = (Rectangle2D) ((Map) nested |
| .get(child2Cell)).get(GraphConstants.BOUNDS); |
| if (graph.getModel().getExecutionType().equals("parallel")) |
| return Double.compare(child1Bounds.getMaxY(), |
| child2Bounds.getMaxY()); |
| else |
| return Double.compare(child1Bounds.getX(), child2Bounds.getX()); |
| } |
| |
| }); |
| changed = ensureNoInternalOverlap(sortedChildren.get(0), nested); |
| Rectangle2D graphRectangle = (Rectangle2D) ((Map) nested |
| .get(this.m_jgAdapter.getVertexCell(sortedChildren.get(0).getModel()))) |
| .get(GraphConstants.BOUNDS); |
| for (int i = 1; i < sortedChildren.size(); i++) { |
| ModelGraph child2 = sortedChildren.get(i); |
| if (ensureNoInternalOverlap(child2, nested)) |
| changed = true; |
| DefaultGraphCell child2Cell = this.m_jgAdapter.getVertexCell(child2 |
| .getModel()); |
| for (int j = i - 1; j >= 0; j--) { |
| ModelGraph child1 = sortedChildren.get(j); |
| DefaultGraphCell child1Cell = this.m_jgAdapter.getVertexCell(child1 |
| .getModel()); |
| Rectangle2D child1Bounds = (Rectangle2D) ((Map) nested |
| .get(child1Cell)).get(GraphConstants.BOUNDS); |
| Rectangle2D child2Bounds = (Rectangle2D) ((Map) nested |
| .get(child2Cell)).get(GraphConstants.BOUNDS); |
| if (child1Bounds.intersects(child2Bounds)) { |
| changed = true; |
| if (graph.getModel().getExecutionType().equals("parallel")) { |
| ((Map) nested.get(child2Cell)).put(GraphConstants.BOUNDS, |
| new AttributeMap.SerializableRectangle2D(child2Bounds.getX(), |
| child1Bounds.getMaxY() + 20.0, child2Bounds.getWidth(), |
| child2Bounds.getHeight())); |
| this.shift(child2.getChildren(), nested, 0, |
| child1Bounds.getMaxY() + 20.0 - child2Bounds.getY()); |
| } else { |
| ((Map) nested.get(child2Cell)).put( |
| GraphConstants.BOUNDS, |
| new AttributeMap.SerializableRectangle2D(child1Bounds |
| .getMaxX() + 20.0, child2Bounds.getY(), child2Bounds |
| .getWidth(), child2Bounds.getHeight())); |
| this.shift(child2.getChildren(), nested, child1Bounds.getMaxX() |
| + 20.0 - child2Bounds.getX(), 0); |
| } |
| } |
| } |
| graphRectangle = graphRectangle.createUnion((Rectangle2D) ((Map) nested |
| .get(child2Cell)).get(GraphConstants.BOUNDS)); |
| } |
| ((Map) nested.get(this.m_jgAdapter.getVertexCell(graph.getModel()))).put( |
| GraphConstants.BOUNDS, new AttributeMap.SerializableRectangle2D( |
| graphRectangle.getX() - 5, graphRectangle.getY() - 20, |
| graphRectangle.getWidth() + 10, graphRectangle.getHeight() + 25)); |
| } |
| return changed; |
| } |
| |
| private void shift(List<ModelGraph> graphs, Map nested, double x, double y) { |
| for (int i = 0; i < graphs.size(); i++) { |
| ModelGraph graph = graphs.get(i); |
| DefaultGraphCell cell = this.m_jgAdapter.getVertexCell(graph.getModel()); |
| Rectangle2D bounds = (Rectangle2D) ((Map) nested.get(cell)) |
| .get(GraphConstants.BOUNDS); |
| ((Map) nested.get(cell)).put( |
| GraphConstants.BOUNDS, |
| new AttributeMap.SerializableRectangle2D(bounds.getX() + x, bounds |
| .getY() + y, bounds.getWidth(), bounds.getHeight())); |
| this.shift(graph.getChildren(), nested, x, y); |
| } |
| } |
| |
| private void addGroups(List<ModelGraph> modelGraphs, Map nested, |
| ViewState state) { |
| for (ModelGraph modelGraph : modelGraphs) |
| this.addGroups(modelGraph, nested, state); |
| } |
| |
| private DefaultGraphCell addGroups(ModelGraph modelGraph, Map nested, |
| ViewState state) { |
| if (modelGraph.getModel().isParentType()) { |
| double top_x = Double.MAX_VALUE, top_y = Double.MAX_VALUE, bottom_x = 0.0, bottom_y = 0.0; |
| this.directedGraph.addVertex(modelGraph.getModel()); |
| DefaultGraphCell modelCell = this.m_jgAdapter.getVertexCell(modelGraph |
| .getModel()); |
| Vector<DefaultGraphCell> group = new Vector<DefaultGraphCell>(); |
| group.add(modelCell); |
| |
| HashMap<Object, Object> map = new HashMap<Object, Object>(); |
| for (int i = 0; i < modelGraph.getChildren().size(); i++) { |
| ModelGraph child = modelGraph.getChildren().get(i); |
| DefaultGraphCell curCell = addGroups(child, nested, state); |
| group.add(curCell); |
| Rectangle2D bounds = (Rectangle2D) ((Map<Object, Object>) nested |
| .get(curCell)).get("bounds"); |
| if (bounds.getX() < top_x) |
| top_x = bounds.getX(); |
| if (bounds.getY() < top_y) |
| top_y = bounds.getY(); |
| if (bounds.getMaxX() > bottom_x) |
| bottom_x = bounds.getMaxX(); |
| if (bounds.getMaxY() > bottom_y) |
| bottom_y = bounds.getMaxY(); |
| } |
| |
| map.put(GraphConstants.BOUNDS, new AttributeMap.SerializableRectangle2D( |
| top_x - 5, top_y - 20, bottom_x - top_x + 10, bottom_y - top_y + 25)); |
| map.put(GraphConstants.FOREGROUND, Color.black); |
| if (modelGraph.getModel().isRef()) |
| map.put(GraphConstants.BACKGROUND, Color.lightGray); |
| else |
| map.put(GraphConstants.BACKGROUND, Color.white); |
| if (modelGraph.isExcused()) |
| map.put(GraphConstants.GRADIENTCOLOR, Color.gray); |
| map.put(GraphConstants.HORIZONTAL_ALIGNMENT, SwingConstants.LEFT); |
| map.put(GraphConstants.VERTICAL_ALIGNMENT, SwingConstants.TOP); |
| map.put(GraphConstants.BORDER, new LineBorder(modelGraph.getModel() |
| .getColor(), 1)); |
| jgraph.getGraphLayoutCache().toBack(new Object[] { modelCell }); |
| nested.put(modelCell, map); |
| return modelCell; |
| } |
| DefaultGraphCell cell = this.m_jgAdapter.getVertexCell(modelGraph |
| .getModel()); |
| ((Map<Object, Object>) nested.get(cell)).put(GraphConstants.FOREGROUND, |
| Color.black); |
| if (modelGraph.isExcused()) |
| ((Map<Object, Object>) nested.get(cell)).put( |
| GraphConstants.GRADIENTCOLOR, Color.gray); |
| else |
| ((Map<Object, Object>) nested.get(cell)).put( |
| GraphConstants.GRADIENTCOLOR, Color.white); |
| if (!((ModelNode) ((DefaultGraphCell) cell).getUserObject()).isRef()) |
| ((Map<Object, Object>) nested.get(cell)).put(GraphConstants.BACKGROUND, |
| modelGraph.getModel().getColor()); |
| else |
| ((Map<Object, Object>) nested.get(cell)).put(GraphConstants.BACKGROUND, |
| Color.lightGray); |
| return cell; |
| } |
| |
| private void removeAllEdges(ObservableGraph<ModelNode, IdentifiableEdge> graph){ |
| List<IdentifiableEdge> edges = new Vector<IdentifiableEdge>(); |
| |
| for(IdentifiableEdge edge: graph.getEdges()){ |
| edges.add(edge); |
| } |
| |
| for(IdentifiableEdge edge: edges){ |
| graph.removeEdge(edge); |
| } |
| } |
| |
| |
| private class Pair { |
| String first, second; |
| |
| public Pair(String first, String second) { |
| this.first = first; |
| this.second = second; |
| } |
| |
| public String getFirst() { |
| return this.first; |
| } |
| |
| public String getSecond() { |
| return this.second; |
| } |
| } |
| |
| public class MyGraphListener implements MouseListener, ActionListener { |
| |
| private Point curPoint; |
| private ViewState state; |
| |
| public MyGraphListener(ViewState state) { |
| this.state = state; |
| } |
| |
| public void mouseClicked(MouseEvent e) { |
| curPoint = e.getPoint(); |
| if (e.getButton() == MouseEvent.BUTTON3) { |
| Object mouseOverCell = GraphView.this.jgraph.getFirstCellForLocation( |
| e.getX(), e.getY()); |
| ModelGraph mouseOverGraph = null; |
| if (mouseOverCell != null) { |
| mouseOverGraph = (GuiUtils.find(state.getGraphs(), |
| ((ModelNode) ((DefaultMutableTreeNode) mouseOverCell) |
| .getUserObject()).getId())); |
| if (mouseOverGraph != null) { |
| if (GuiUtils.isDummyNode(mouseOverGraph.getModel())) { |
| mouseOverGraph = mouseOverGraph.getParent(); |
| } else { |
| while (mouseOverGraph != null |
| && mouseOverGraph.getParent() != null |
| && mouseOverGraph.getParent().getModel().isRef()) |
| mouseOverGraph = mouseOverGraph.getParent(); |
| } |
| if (mouseOverGraph != null) |
| mouseOverCell = GraphView.this.m_jgAdapter |
| .getVertexCell(mouseOverGraph.getModel()); |
| else |
| mouseOverCell = null; |
| } |
| state.setSelected(mouseOverGraph); |
| } else { |
| state.setSelected(null); |
| } |
| PopupMenu actionsMenu = createActionMenu(state); |
| GraphView.this.notifyListeners(); |
| GraphView.this.jgraph.add(actionsMenu); |
| actionsMenu.show(GraphView.this.jgraph, e.getPoint().x, e.getPoint().y); |
| } else if (e.getButton() == MouseEvent.BUTTON1) { |
| if (state.getMode() == View.Mode.ZOOM_IN) { |
| state.setProperty(SCALE, Double.toString(Double.parseDouble(state |
| .getFirstPropertyValue(SCALE)) + 0.1)); |
| state.setSelected(null); |
| GraphView.this.notifyListeners(); |
| } else if (state.getMode() == View.Mode.ZOOM_OUT) { |
| state.setProperty(SCALE, Double.toString(Double.parseDouble(state |
| .getFirstPropertyValue(SCALE)) - 0.1)); |
| state.setSelected(null); |
| GraphView.this.notifyListeners(); |
| } else if (state.getMode() == View.Mode.EDIT) { |
| Object cell = GraphView.this.jgraph.getFirstCellForLocation(e.getX(), |
| e.getY()); |
| if (cell != null) { |
| if (cell instanceof DefaultEdge) { |
| } else if (cell instanceof DefaultGraphCell) { |
| ModelGraph graph = GuiUtils.find(state.getGraphs(), |
| ((ModelNode) ((DefaultGraphCell) cell).getUserObject()) |
| .getId()); |
| if (graph.getModel().isRef()) |
| while (graph.getParent() != null |
| && graph.getParent().getModel().isRef()) |
| graph = graph.getParent(); |
| if (GuiUtils.isDummyNode(graph.getModel())) |
| graph = graph.getParent(); |
| state.setSelected(graph); |
| GraphView.this.notifyListeners(); |
| } |
| } else if (cell == null && state.getSelected() != null) { |
| state.setSelected(null); |
| GraphView.this.notifyListeners(); |
| } |
| } else if (state.getMode() == View.Mode.DELETE |
| && e.getClickCount() == 2) { |
| Object cell = GraphView.this.jgraph.getFirstCellForLocation(e.getX(), |
| e.getY()); |
| if (cell != null) { |
| if (cell instanceof DefaultEdge) { |
| // do nothing |
| } else if (cell instanceof DefaultGraphCell) { |
| ModelGraph graph = GuiUtils.removeNode(state.getGraphs(), |
| (ModelNode) ((DefaultGraphCell) cell).getUserObject()); |
| GraphView.this.notifyListeners(); |
| } |
| } |
| } |
| } |
| } |
| |
| public void mouseEntered(MouseEvent e) { |
| if (state.getMode() == View.Mode.ZOOM_IN |
| || state.getMode() == View.Mode.ZOOM_OUT) { |
| Toolkit toolkit = Toolkit.getDefaultToolkit(); |
| try { |
| GraphView.this.jgraph.setCursor(toolkit.createCustomCursor( |
| IconLoader.getIcon(IconLoader.ZOOM_CURSOR), new Point(0, 0), |
| "img")); |
| } catch (Exception e1) { |
| e1.printStackTrace(); |
| } |
| } else if (state.getMode() == Mode.MOVE) { |
| GraphView.this.jgraph.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| } else { |
| GraphView.this.jgraph.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); |
| } |
| } |
| |
| public void mouseExited(MouseEvent e) { |
| } |
| |
| public void mousePressed(MouseEvent e) { |
| } |
| |
| public void mouseReleased(MouseEvent e) { |
| } |
| |
| public void actionPerformed(ActionEvent e) { |
| this.createNewGraph(e.getActionCommand()); |
| } |
| |
| private void createNewGraph(String actionCommand) { |
| ModelGraph newGraph = null; |
| if (actionCommand.equals(NEW_TASK_ITEM_NAME)) { |
| newGraph = new ModelGraph(new ModelNode(state.getFile(), |
| GuiUtils.createUniqueName())); |
| } else if (actionCommand.equals(NEW_PARALLEL_ITEM_NAME)) { |
| ModelNode node = new ModelNode(state.getFile(), |
| GuiUtils.createUniqueName()); |
| node.setExecutionType("parallel"); |
| newGraph = new ModelGraph(node); |
| } else if (actionCommand.equals(NEW_SEQUENTIAL_ITEM_NAME)) { |
| ModelNode node = new ModelNode(state.getFile(), |
| GuiUtils.createUniqueName()); |
| node.setExecutionType("sequential"); |
| newGraph = new ModelGraph(node); |
| } else { |
| return; |
| } |
| Object cell = GraphView.this.jgraph.getSelectionCell(); |
| if (cell != null) { |
| if (cell instanceof DefaultGraphCell) { |
| ModelGraph graph = GuiUtils.find(state.getGraphs(), |
| ((ModelNode) ((DefaultGraphCell) cell).getUserObject()).getId()); |
| if (graph != null) |
| graph.addChild(newGraph); |
| } |
| } else { |
| state.addGraph(newGraph); |
| GraphView.this.setShift(state, newGraph, curPoint); |
| } |
| GraphView.this.notifyListeners(); |
| } |
| |
| } |
| |
| public void setShift(ViewState state, ModelGraph modelGraph, Point point) { |
| state.setProperty(modelGraph.getModel().getId() + "/Shift/x", |
| Integer.toString(point.x)); |
| state.setProperty(modelGraph.getModel().getId() + "/Shift/y", |
| Integer.toString(point.y)); |
| } |
| |
| public void removeShift(ViewState state, ModelGraph modelGraph) { |
| state.removeProperty(modelGraph.getModel().getId() + "/Shift"); |
| } |
| |
| public Point getShift(ViewState state, DefaultGraphCell cell, Map nested) { |
| ModelGraph graph = null; |
| if (cell instanceof DefaultEdge) { |
| IdentifiableEdge edge = (IdentifiableEdge) cell.getUserObject(); |
| Pair pair = GraphView.this.edgeMap.get(edge.id); |
| graph = GuiUtils.find(state.getGraphs(), pair.getFirst()); |
| } else { |
| graph = GuiUtils.find(state.getGraphs(), |
| ((ModelNode) cell.getUserObject()).getId()); |
| } |
| ModelGraph parent = GuiUtils.findRoot(state.getGraphs(), graph); |
| Point shiftPoint = null; |
| if (state.containsProperty(parent.getModel().getId() + "/Shift")) |
| shiftPoint = new Point(Integer.parseInt(state |
| .getFirstPropertyValue(parent.getModel().getId() + "/Shift/x")), |
| Integer.parseInt(state.getFirstPropertyValue(parent.getModel() |
| .getId() + "/Shift/y"))); |
| if (shiftPoint == null) { |
| shiftPoint = new Point(100, 100); |
| this.setShift(state, parent, shiftPoint); |
| return shiftPoint; |
| } else { |
| Rectangle2D bounds = (Rectangle2D) ((Map<Object, Object>) nested |
| .get(GraphView.this.m_jgAdapter.getVertexCell(parent.getModel()))) |
| .get(GraphConstants.BOUNDS); |
| return new Point(shiftPoint.x - (int) bounds.getX(), shiftPoint.y |
| - (int) bounds.getY()); |
| } |
| } |
| |
| |
| private PopupMenu createActionMenu(final ViewState state) { |
| PopupMenu actionsMenu = new PopupMenu(ACTIONS_POP_MENU_NAME); |
| PopupMenu newSubMenu = new PopupMenu(NEW_SUB_POP_MENU_NAME); |
| MenuItem taskItem = new MenuItem(NEW_TASK_ITEM_NAME); |
| MenuItem condItem = new MenuItem(NEW_CONDITION_ITEM_NAME); |
| newSubMenu.add(taskItem); |
| newSubMenu.add(condItem); |
| newSubMenu.add(new MenuItem(NEW_PARALLEL_ITEM_NAME)); |
| newSubMenu.add(new MenuItem(NEW_SEQUENTIAL_ITEM_NAME)); |
| newSubMenu.addActionListener(this.myGraphListener); |
| actionsMenu.add(newSubMenu); |
| MenuItem viewReferrencedWorkflow = new MenuItem(VIEW_REF_WORKFLOW); |
| actionsMenu.add(viewReferrencedWorkflow); |
| MenuItem deleteItem = new MenuItem(DELETE_ITEM_NAME); |
| actionsMenu.add(deleteItem); |
| MenuItem formatItem = new MenuItem(FORMAT_ITEM_NAME); |
| actionsMenu.add(formatItem); |
| |
| PopupMenu orderSubMenu = new PopupMenu(ORDER_SUB_POP_MENU_NAME); |
| |
| ModelGraph modelGraph = state.getSelected(); |
| newSubMenu.setEnabled(modelGraph == null |
| || modelGraph.getModel().isParentType()); |
| deleteItem.setEnabled(modelGraph != null); |
| formatItem.setEnabled(true); |
| if (modelGraph != null) { |
| viewReferrencedWorkflow.setEnabled(modelGraph.getModel().isRef()); |
| taskItem.setEnabled(!modelGraph.isCondition()); |
| condItem.setEnabled(modelGraph.isCondition()); |
| orderSubMenu.setEnabled(modelGraph.getParent() != null |
| && !(modelGraph.isCondition() && !modelGraph.getParent() |
| .isCondition())); |
| } else { |
| boolean isCondition = false; |
| if (state.getGraphs().size() > 0) |
| isCondition = state.getGraphs().get(0).isCondition(); |
| viewReferrencedWorkflow.setEnabled(false); |
| taskItem.setEnabled(!isCondition); |
| condItem.setEnabled(isCondition); |
| } |
| |
| actionsMenu.addActionListener(new ActionListener() { |
| public void actionPerformed(ActionEvent e) { |
| if (e.getActionCommand().equals(DELETE_ITEM_NAME)) { |
| GuiUtils.removeNode(state.getGraphs(), state.getSelected().getModel()); |
| state.setSelected(null); |
| GraphView.this.notifyListeners(); |
| } else if (e.getActionCommand().equals(FORMAT_ITEM_NAME)) { |
| GraphView.this.refreshView(state); |
| } else if (e.getActionCommand().equals(VIEW_REF_WORKFLOW)) { |
| scrollSelectedToVisible = true; |
| GraphView.this.notifyListeners(new ViewChange.VIEW_MODEL(state |
| .getSelected().getModel().getModelId(), GraphView.this)); |
| } |
| } |
| }); |
| PopupMenu edgesSubMenu = new PopupMenu(EDGES_SUB_POP_MENU_NAME); |
| edgesSubMenu.add(new MenuItem(TASK_LEVEL)); |
| edgesSubMenu.add(new MenuItem(WORKFLOW_LEVEL)); |
| actionsMenu.add(edgesSubMenu); |
| edgesSubMenu.addActionListener(new ActionListener() { |
| |
| public void actionPerformed(ActionEvent e) { |
| if (e.getActionCommand().equals(TASK_LEVEL)) { |
| state.setProperty(EDGE_DISPLAY_MODE, TASK_MODE); |
| } else if (e.getActionCommand().equals(WORKFLOW_LEVEL)) { |
| state.setProperty(EDGE_DISPLAY_MODE, WORKFLOW_MODE); |
| } |
| GraphView.this.refreshView(state); |
| } |
| |
| }); |
| actionsMenu.add(orderSubMenu); |
| orderSubMenu.add(new MenuItem(TO_FRONT_ITEM_NAME)); |
| orderSubMenu.add(new MenuItem(TO_BACK_ITEM_NAME)); |
| orderSubMenu.add(new MenuItem(FORWARD_ITEM_NAME)); |
| orderSubMenu.add(new MenuItem(BACKWARDS_ITEM_NAME)); |
| orderSubMenu.addActionListener(new ActionListener() { |
| |
| public void actionPerformed(ActionEvent e) { |
| ModelGraph graph = state.getSelected(); |
| ModelGraph parent = graph.getParent(); |
| if (e.getActionCommand().equals(TO_FRONT_ITEM_NAME)) { |
| if (parent.getChildren().remove(graph)) |
| parent.getChildren().add(0, graph); |
| } else if (e.getActionCommand().equals(TO_BACK_ITEM_NAME)) { |
| if (parent.getChildren().remove(graph)) |
| parent.getChildren().add(graph); |
| } else if (e.getActionCommand().equals(FORWARD_ITEM_NAME)) { |
| int index = parent.getChildren().indexOf(graph); |
| if (index != -1) { |
| parent.getChildren().remove(index); |
| parent.getChildren().add( |
| Math.min(parent.getChildren().size(), index + 1), graph); |
| } |
| } else if (e.getActionCommand().equals(BACKWARDS_ITEM_NAME)) { |
| int index = parent.getChildren().indexOf(graph); |
| if (index != -1) { |
| parent.getChildren().remove(index); |
| parent.getChildren().add(Math.max(0, index - 1), graph); |
| } |
| } |
| GraphView.this.notifyListeners(); |
| } |
| |
| }); |
| return actionsMenu; |
| } |
| |
| } |