package vg.userInterface.jgraphx;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Observer;
import java.util.Set;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import com.mxgraph.analysis.mxGraphAnalysis;
import com.mxgraph.analysis.mxICostFunction;
import com.mxgraph.layout.mxIGraphLayout;
import com.mxgraph.layout.mxParallelEdgeLayout;
import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.mxGraphOutline;
import com.mxgraph.swing.handler.mxKeyboardHandler;
import com.mxgraph.swing.handler.mxRubberband;
import com.mxgraph.swing.util.mxMorphing;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxUndoManager;
import com.mxgraph.util.mxUndoableEdit;
import com.mxgraph.util.mxUtils;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.util.mxUndoableEdit.mxUndoableChange;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;
import vg.core.AModel;
import vg.core.AUserInterface;
import vg.core.IGraphView;
import vg.core.VisualGraph;
import vg.core.graph.SubGraph;
import vg.core.request.IUIRequestOwner;
import vg.core.request.UIRequestGoToInAIF;
import vg.core.request.UIRequestOpenSubGraph;
import vg.core.storableGraph.StorableAttribute;
import vg.core.storableGraph.StorableEdge;
import vg.core.storableGraph.StorableSubGraph;
import vg.core.storableGraph.StorableVertex;
import vg.userInterface.core.IGraphLayoutManager;
import vg.userInterface.swingComponents.ZoomComboBox;
/**
* This class is wrapper to jgraphx library It contains swing components which draw graph and minimap
*
* @author dkolbin
*/
public class JGraphView implements IGraphView {
private mxGraphComponent graphComponent = null;
private mxGraphOutline graphOutline = null;
private mxUndoManager undoManager;
private mxIEventListener scaleTracker = null;
private final StorableSubGraph subGraph;
private AUserInterface userInterface = null;
private IUIRequestOwner requestOwner;
private mxGraph currentGraph;
private JPopupMenu popup;
private JMenu connectorPopup;
// -------------------------------------------
private HashMap<mxCell, Integer> subgraphsid;
private HashMap<mxCell, HashMap<StorableAttribute, Boolean>> edgeAttributes;
private HashMap<mxCell, HashMap<StorableAttribute, Boolean>> vertexAttributes;
private HashMap<Integer, Connection> connections;
private int graphId = -1;
// -------------------------------------------
private ArrayList<Observer> observers;
private final AModel model;
private IGraphLayoutManager layoutManager;
// Title of graph view
private String title = null;
private ActionListener settingListner = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Object parent = currentGraph.getDefaultParent();
currentGraph.getModel().beginUpdate();
for (Object cell : currentGraph.getChildCells(parent)) {
mxCellState state = currentGraph.getView().getState(cell);
state.setStyle(currentGraph.getCellStyle(cell));
currentGraph.updateCellSize(cell);
}
currentGraph.getModel().endUpdate();
currentGraph.repaint();
}
};
// -------------------------------------------------------------------------
// private constructor
// -------------------------------------------------------------------------
private JGraphView(AUserInterface userInterface, final StorableSubGraph subGraph) {
// set model, which may use for getting different information.
if (userInterface != null) {
this.model = userInterface.getModel();
} else {
this.model = null;
}
this.subGraph = subGraph;
this.observers = new ArrayList<Observer>();
this.userInterface = userInterface;
this.layoutManager = this.userInterface.getGraphLayoutManager();
this.edgeAttributes = new HashMap<mxCell, HashMap<StorableAttribute, Boolean>>();
this.vertexAttributes = new HashMap<mxCell, HashMap<StorableAttribute, Boolean>>();
this.connections = new HashMap<Integer, Connection>();
// ---------------------------------------
buildTitleAndGraphId(); // find graphId, graph view's title
// ---------------------------------------
currentGraph = new mxGraph();
undoManager = new mxUndoManager();
// deny clone cells
currentGraph.setCellsCloneable(false);
currentGraph.setGridEnabled(false);
currentGraph.setCellsDeletable(false);
currentGraph.setHtmlLabels(true);
currentGraph.setMultigraph(true);
// deny changing edge targets
currentGraph.setCellsDisconnectable(false);
// deny edge which doesn't connect with some cells
currentGraph.setAllowDanglingEdges(false);
currentGraph.setCellsEditable(false);
currentGraph.setGridSize(4);
currentGraph.getModel().addListener(mxEvent.UNDO, undoHandle);
currentGraph.getView().addListener(mxEvent.UNDO, undoHandle);
// Set default style for vertexes
currentGraph.setStylesheet(JGraphSettings.getStylesheet());
JGraphSettings.addListner(settingListner);
this.requestOwner = new IUIRequestOwner() {
public void callRequestOwner(Object obj) {
// TODO
}
};
guiSetup();
}
// -------------------------------------------------------------------------
// public constructors
// -------------------------------------------------------------------------
/**
* Construct new JGraphView object from another, which contain only those vertexes and edges, which have subGraph. New view will be
* zoomed. This constructor also copy all visualized attributes from old view into new.
*
* @param view
* - old view
* @param subGraph
* - subgraph,
*/
public JGraphView(JGraphView view, StorableSubGraph subGraph) {
this(view.userInterface, subGraph);
this.userInterface = view.userInterface;
// ---------------------------------------
Object parent = this.currentGraph.getDefaultParent();
List<StorableVertex> vertexes = this.subGraph.getVertices();
Object[] cells;
if (view.isSelected())
cells = view.currentGraph.getSelectionCells();
else
cells = view.currentGraph.getChildVertices(view.currentGraph.getDefaultParent());
HashMap<String, mxCell> id2vert = new HashMap<String, mxCell>();
this.subgraphsid = new HashMap<mxCell, Integer>();
currentGraph.getModel().beginUpdate();
for (Object cell : cells) {
if (((mxCell) cell).isVertex()) {
for (StorableVertex v : vertexes) {
if (((mxCell) cell).getId().equals("v" + v.getStorableId())) {
mxCell c = (mxCell) cell;
mxCell c2 = (mxCell) currentGraph.insertVertex(parent, c.getId(), c.getValue(), c.getGeometry().getX(), c
.getGeometry().getY(), c.getGeometry().getWidth(), c.getGeometry().getHeight(), c.getStyle());
id2vert.put(c.getId(), c2);
if (view.subgraphsid.get(c) != null) {
this.subgraphsid.put((mxCell) c2, view.subgraphsid.get(c));
}
HashMap<StorableAttribute, Boolean> m = view.vertexAttributes.get(c);
HashMap<StorableAttribute, Boolean> m2 = new HashMap<StorableAttribute, Boolean>();
for (StorableAttribute s : m.keySet()) {
m2.put(s, m.get(s));
}
vertexAttributes.put(c2, m2);
break;
}
}
}
}
cells = view.currentGraph.getAllEdges(cells);
for (StorableEdge edge : this.subGraph.getEdges()) {
for (Object cell : cells) {
mxCell c = (mxCell) cell;
mxCell source = (mxCell) c.getSource();
mxCell target = (mxCell) c.getTarget();
if (source == null || target == null)
continue;
if (!source.getId().equals("v" + edge.getStorableSource().getStorableId())
|| !target.getId().equals("v" + edge.getStorableTarget().getStorableId()))
continue;
Object source2 = id2vert.get(source.getId());
Object target2 = id2vert.get(target.getId());
if (source2 != null && target2 != null) {
mxCell c2 = (mxCell) currentGraph.insertEdge(parent, c.getId(), c.getValue(), source2, target2, c.getStyle());
c2.setGeometry(c.getGeometry());
HashMap<StorableAttribute, Boolean> m = view.edgeAttributes.get(c);
HashMap<StorableAttribute, Boolean> m2 = new HashMap<StorableAttribute, Boolean>();
for (StorableAttribute s : m.keySet()) {
m2.put(s, m.get(s));
}
edgeAttributes.put(c2, m2);
break;
}
}
}
currentGraph.getModel().endUpdate();
double x = 0;
double y = 0;
cells = currentGraph.getChildCells(currentGraph.getDefaultParent());
boolean initialize = false;
for (Object cell : cells) {
mxCellState state = currentGraph.getView().getState(cell);
if (!initialize) {
x = state.getLabelBounds().getX();
y = state.getLabelBounds().getY();
initialize = true;
}
if (state.getLabelBounds().getX() < x)
x = state.getLabelBounds().getX();
if (state.getLabelBounds().getY() < y)
y = state.getLabelBounds().getY();
}
if (initialize)
currentGraph.moveCells(currentGraph.getChildCells(currentGraph.getDefaultParent()), -x, -y);
double zoomfactor = currentGraph.getView().getScale();
double zoomfactorx = zoomfactor / currentGraph.getView().getGraphBounds().getWidth() * view.graphComponent.getWidth();
double zoomfactory = zoomfactor / currentGraph.getView().getGraphBounds().getHeight() * view.graphComponent.getHeight();
currentGraph.getView().setScale(Math.max(Math.min(zoomfactorx, zoomfactory), zoomfactor));
}
/**
* Standard constructor for JGraphView which load
*
* @param g
* - subgraph, which should be showed
*/
public JGraphView(final StorableSubGraph g, final AUserInterface userInterface) {
this(userInterface, g);
this.loadGraph();
}
// -------------------------------------------------------------------------
// public methods
// -------------------------------------------------------------------------
public JComponent getGraphRepresentation() {
return graphComponent;
}
public JComponent getMiniMap() {
return graphOutline;
}
public void setZoom(String scale) {
if (scale.equals("Actual size")) {
graphComponent.zoomActual();
} else {
try {
scale = scale.replace("%", "");
graphComponent.zoomTo(Double.parseDouble(scale) / 100, true);
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex.getMessage());
}
}
}
public void SetEnableZoomComboToGraphView(boolean enable, final ZoomComboBox zoomCombo) {
if (enable) {
scaleTracker = new mxIEventListener() {
public void invoke(final Object sender, final mxEventObject evt) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
invoke(sender, evt);
}
});
return;
}
final mxGraphView view = graphComponent.getGraph().getView();
double scale = view.getScale();
if (scale > 100) {
scale = 100;
graphComponent.zoomTo(scale, true);
}
zoomCombo.setSelectedItem(Math.round(100 * scale) + "%");
}
};
graphComponent.getGraph().getView().addListener(mxEvent.SCALE, scaleTracker);
graphComponent.getGraph().getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleTracker);
scaleTracker.invoke(null, null);
} else {
graphComponent.getGraph().getView().removeListener(scaleTracker);
}
}
public void zoomIn() {
graphComponent.zoomIn();
}
public void zoomOut() {
graphComponent.zoomOut();
}
public void redo() {
undoManager.redo();
}
public void undo() {
undoManager.undo();
}
public StorableSubGraph getStorableSubGraph() {
return (this.subGraph);
}
public SubGraph getSubGraph() {
return (this.subGraph.getSubGraph());
}
public void showAttributes(final Set<String> vertexAttr, final Set<String> edgeAttr) {
Object[] cells;
if (currentGraph.isSelectionEmpty()) {
cells = currentGraph.getChildCells(currentGraph.getDefaultParent());
} else {
cells = currentGraph.getSelectionCells();
}
currentGraph.getModel().beginUpdate();
// vertexes-------------------------------
for (Object cell : cells) {
Map<StorableAttribute, Boolean> attrs = this.vertexAttributes.get(cell);
if (attrs != null) {
for (StorableAttribute a : attrs.keySet()) {
attrs.put(a, new Boolean(false));
}
for (StorableVertex v : subGraph.getVertices()) {
if (((mxCell) cell).getId().equals("v" + v.getStorableId())) {
String ss = "";
for (String str : vertexAttr) {
String s = null;
Collection<StorableAttribute> listAttribute = v.getAttributes();
if (listAttribute != null) {
for (StorableAttribute bufAttriute : listAttribute) {
if (bufAttriute.getName().equals(str)) {
s = bufAttriute.getValue();
attrs.put(bufAttriute, new Boolean(true));
}
}
if (s != null)
if (str.equals("name"))
ss = "<b>" + s.replaceAll("\\n", "<br>")+ "</b>" + "<br>" + ss;
else
ss += "<b>" + str + "</b>" + ":" + s.replaceAll("\\n", "<br>") + "<br>";
}
}
if (ss.length() > 0) {
ss = "<html>" +
// "<center><font color=black size=2><b><" + v.getId() + "></b></font></center>" +
ss + "</html>";
}
((mxCell) cell).setValue(ss);
currentGraph.updateCellSize(cell);
break;
}
}
}
currentGraph.getView().getState(cell).setStyle(currentGraph.getCellStyle(cell));
}
// edges----------------------------------
for (Object cell : cells) {
Map<StorableAttribute, Boolean> attrs = this.edgeAttributes.get(cell);
if (attrs != null) {
for (StorableAttribute a : attrs.keySet()) {
attrs.put(a, new Boolean(false));
}
String ss = "";
for (StorableEdge e : subGraph.getEdges()) {
if (((mxCell) cell).getId().equals("e" + e.getStorableId())) {
for (String str : edgeAttr) {
String s = null;
Collection<StorableAttribute> listAttribute = e.getStorableAttributes();
if (listAttribute != null) {
for (StorableAttribute bufAttriute : listAttribute) {
if (bufAttriute.getName().equals(str)) {
s = bufAttriute.getValue();
attrs.put(bufAttriute, new Boolean(true));
}
}
}
if (s != null)
ss += "<b>" + str + "</b>" + ":" + s.replaceAll("\\n", "<br>") + "<br>";
}
((mxCell) cell).setValue("<html>" + ss + "</html>");
break;
}
}
}
}
currentGraph.getModel().endUpdate();
double x = 0;
double y = 0;
cells = currentGraph.getChildCells(currentGraph.getDefaultParent());
for (Object cell : cells) {
mxCellState state = currentGraph.getView().getState(cell);
if (state != null) {
currentGraph.getView().updateLabelBounds(state);
if (state.getLabelBounds().getX() < x)
x = state.getLabelBounds().getX();
if (state.getLabelBounds().getY() < y)
y = state.getLabelBounds().getY();
}
}
if (x != 0 || y != 0)
currentGraph.moveCells(currentGraph.getChildCells(currentGraph.getDefaultParent()), -x, -y);
currentGraph.repaint();
}
public StorableSubGraph getSelectionSubGraph() {
if (!isSelected())
return null;
ArrayList<StorableVertex> vertexes = new ArrayList<StorableVertex>();
Object[] cells = currentGraph.getSelectionCells();
if (cells.length == 0) {
return (null);
}
for (Object cell : cells) {
if (((mxCell) cell).isVertex()) {
for (StorableVertex v : subGraph.getVertices()) {
if (((mxCell) cell).getId().equals("v" + v.getStorableId())) {
vertexes.add(v);
break;
}
}
}
}
List<StorableEdge> edges = getAllEdgesBeetweenVertexes(vertexes);
return (new StorableSubGraph(subGraph.getName(), vertexes, edges, subGraph.isDirected()));
}
public boolean isSelected() {
return !this.currentGraph.isSelectionEmpty();
}
public Map<StorableAttribute, Boolean> getEdgeAttributes() {
HashMap<StorableAttribute, Boolean> map = new HashMap<StorableAttribute, Boolean>();
Object[] cells;
if (currentGraph.isSelectionEmpty()) {
cells = currentGraph.getChildEdges(currentGraph.getDefaultParent());
} else {
cells = currentGraph.getSelectionCells();
}
for (Object cell : cells) {
Map<StorableAttribute, Boolean> attrs = this.edgeAttributes.get((mxCell) cell);
if (attrs != null)
map.putAll(attrs);
}
return map;
}
public Map<StorableAttribute, Boolean> getVertexAttributes() {
HashMap<StorableAttribute, Boolean> map = new HashMap<StorableAttribute, Boolean>();
Object[] cells;
if (currentGraph.isSelectionEmpty()) {
cells = currentGraph.getChildVertices(currentGraph.getDefaultParent());
} else {
cells = currentGraph.getSelectionCells();
}
for (Object cell : cells) {
Map<StorableAttribute, Boolean> attrs = this.vertexAttributes.get((mxCell) cell);
if (attrs != null) {
map.putAll(attrs);
}
}
return map;
}
public void executeLayout() {
currentGraph.getModel().beginUpdate();
Object layout = layoutManager.getDefaultLayout().getLayout(this);
if (layout instanceof mxIGraphLayout) {
mxIGraphLayout l = (mxIGraphLayout) layout;
l.execute(currentGraph.getDefaultParent());
}
if (layoutManager.isAnimated()) {
mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20);
morph.addListener(mxEvent.DONE, new mxIEventListener() {
public void invoke(Object sender, mxEventObject evt) {
currentGraph.getModel().endUpdate();
}
});
morph.startAnimation();
} else {
currentGraph.getModel().endUpdate();
}
mxParallelEdgeLayout paralLay = new mxParallelEdgeLayout(currentGraph);
paralLay.execute(currentGraph.getDefaultParent());
// sometimes layout can put vertex out of minimap, that should fix it
double x = 0, y = 0;
boolean init = false;
for (Object o : currentGraph.getChildCells(currentGraph.getDefaultParent())) {
mxCell cell = (mxCell) o;
if (cell.isVertex()) {
if (cell.getGeometry().getX() < x || !init)
x = cell.getGeometry().getX();
if (cell.getGeometry().getY() < y || !init)
y = cell.getGeometry().getY();
} else if (cell.getGeometry().getPoints() != null)
for (mxPoint point : cell.getGeometry().getPoints()) {
if (point.getX() < x || !init)
x = point.getX();
if (point.getY() < y || !init)
y = point.getY();
}
init = true;
}
if (init)
currentGraph.moveCells(currentGraph.getChildCells(currentGraph.getDefaultParent()), -x, -y);
}
public void setInvisibleEdges(Set<Integer> edgeIds) {
Object parent = currentGraph.getDefaultParent();
Object[] cells = currentGraph.getChildEdges(parent);
ArrayList<Object> visiblecells = new ArrayList<Object>();
ArrayList<Object> invisiblecells = new ArrayList<Object>();
for (Object cell : cells) {
if (cell instanceof mxCell) {
boolean inv = false;
mxCell c = (mxCell) cell;
for (StorableEdge e : subGraph.getEdges()) {
if (c.getId().equals("e" + e.getStorableId()) && edgeIds.contains(Integer.valueOf(e.getStorableId()))) {
invisiblecells.add(c);
inv = true;
break;
}
}
if (!inv)
visiblecells.add(c);
}
}
mxUtils.setCellStyles(currentGraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_OPACITY, "20");
mxUtils.setCellStyles(currentGraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "20");
mxUtils.setCellStyles(currentGraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_OPACITY, "100");
mxUtils.setCellStyles(currentGraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "100");
}
public void setInvisibleVertices(Set<Integer> vertexIds) {
Object parent = currentGraph.getDefaultParent();
Object[] cells = currentGraph.getChildVertices(parent);
ArrayList<Object> visiblecells = new ArrayList<Object>();
ArrayList<Object> invisiblecells = new ArrayList<Object>();
for (Object cell : cells) {
if (cell instanceof mxCell) {
boolean inv = false;
mxCell c = (mxCell) cell;
for (StorableVertex v : subGraph.getVertices()) {
if (c.getId().equals("v" + v.getStorableId()) && vertexIds.contains(v.getStorableId())) {
invisiblecells.add(c);
inv = true;
break;
}
}
if (!inv)
visiblecells.add(c);
}
}
mxUtils.setCellStyles(currentGraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_OPACITY, "20");
mxUtils.setCellStyles(currentGraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "20");
mxUtils.setCellStyles(currentGraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_OPACITY, "100");
mxUtils.setCellStyles(currentGraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "100");
}
// -------------------------------------------------------------------------
public void addObserver(Observer o) {
synchronized (this.observers) {
this.observers.add(o);
}
}
public void deleteAllObservers() {
synchronized (this.observers) {
this.observers.clear();
}
}
public void deleteObserver(Observer o) {
synchronized (this.observers) {
this.observers.remove(o);
}
}
mxGraph getGraph() {
return currentGraph;
}
public void showCycles(final StorableVertex vertex) {
ArrayList<Object> cicle = findPath(vertex, vertex);
if (cicle != null && cicle.size() > 1) {
currentGraph.setSelectionCells(cicle);
} else {
VisualGraph.windowMessage.infoMessage("Cicle was not found with vertex " + vertex.getId(), "Cicle messsage");
}
}
public void showCriticalPath(final StorableVertex source, final StorableVertex target, final boolean maximum, final String attrName) {
mxCell from = null;
mxCell to = null;
Object parent = currentGraph.getDefaultParent();
Object[] cells = currentGraph.getChildVertices(parent);
for (Object c : cells) {
if (c instanceof mxCell) {
mxCell cell = (mxCell) c;
if (cell.getId().equals("v" + source.getStorableId())) {
from = cell;
}
if (cell.getId().equals("v" + target.getStorableId())) {
to = cell;
}
}
}
if (from == null || to == null)
return;
mxGraphAnalysis mga = mxGraphAnalysis.getInstance();
Object[] path = mga.getShortestPath(currentGraph, from, to, new mxICostFunction() {
@Override
public double getCost(mxCellState state) {
double length = 0;
boolean attrExist = false;
if (JGraphView.this.edgeAttributes.get(state.getCell()) == null)
return Double.POSITIVE_INFINITY;
for (StorableAttribute attr : JGraphView.this.edgeAttributes.get(state.getCell()).keySet()) {
if (attrName.equals(attr.getName())) {
length = Double.valueOf(attr.getValue());
attrExist = true;
}
}
if (!attrExist)
return Double.POSITIVE_INFINITY;
if (maximum)
return 1 / length;
else
return length;
}
}, currentGraph.getChildEdges(parent).length, subGraph.isDirected());
if (path.length > 0) {
currentGraph.setSelectionCells(path);
} else {
VisualGraph.windowMessage.infoMessage("Critical path was not found between vertexes " + source.getId() + " to "
+ target.getId(), "Critical path messsage");
}
currentGraph.setSelectionCells(path);
}
public void showPaths(final StorableVertex source, final StorableVertex target) {
ArrayList<Object> paths = findPath(source, target);
if (paths != null && paths.size() > 0) {
currentGraph.setSelectionCells(paths);
} else {
VisualGraph.windowMessage.infoMessage("Path was not found beetween vertexes " + source.getId() + " to " + target.getId(),
"Path messsage");
}
}
public void setSelectionElements(final List<Integer> lvsids, final List<Integer> lesids) {
ArrayList<Object> selection = new ArrayList<Object>();
Object parent = currentGraph.getDefaultParent();
if (lvsids != null) {
for (Object ob : currentGraph.getChildVertices(parent)) {
if (ob instanceof mxCell) {
mxCell cell = (mxCell) ob;
int sid = Integer.parseInt(cell.getId().substring(1));
for (Integer i : lvsids) {
if (i.equals(sid)) {
selection.add(cell);
break;
}
}
}
}
}
if (lesids != null) {
for (Object ob : currentGraph.getChildEdges(parent)) {
if (ob instanceof mxCell) {
mxCell cell = (mxCell) ob;
int sid = Integer.parseInt(cell.getId().substring(1));
for (Integer i : lesids) {
if (i.equals(sid)) {
selection.add(cell);
break;
}
}
}
}
}
currentGraph.setSelectionCells(selection);
}
public String getTitle() {
return (this.title);
}
@Override
public void addNewConnect(int connectId, String connectName, String attributeName, int graphId) {
if (graphId != this.graphId)
return;
Connection c = new Connection();
c.connectId = connectId;
c.connectName = connectName;
c.attributeName = attributeName;
this.connections.put(connectId, c);
}
@Override
public void removeConnect(int connectId) {
this.connections.remove(connectId);
}
// /////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
// /////////////////////////////////////////////////////////////////////////
/**
* This method creates shortName and fullName. Need: this.model, this.subgraph.
*/
private void buildTitleAndGraphId() {
if (this.subGraph != null && this.model != null) {
List<StorableVertex> lsv = this.subGraph.getVertices();
if (lsv != null && lsv.size() != 0) {
// find subgraph id
StorableVertex sv = lsv.get(0);
// int gid = -1; // graph id
int vid = sv.getStorableId(); // vertex id
int sid = -1; // subgraph id
String firstRequest = "select s1.db_id_subgraph " + "from com_subgraph_vertex s1 "
+ "where s1.db_id_vertex = " + Integer.valueOf(vid).toString() + ";";
List<List<Object>> firstResult = this.model.executeSQLRequest(firstRequest);
if (firstResult != null && !firstResult.isEmpty()) {
sid = (Integer) firstResult.get(0).get(0);
}
// find graph id
String thirdRequest = "select distinct s1.db_id_graph " + "from com_graph_subgraph s1 "
+ "where s1.db_id_subgraph = " + Integer.valueOf(sid) + ";";
List<List<Object>> thirdResult = this.model.executeSQLRequest(thirdRequest);
if (thirdResult != null && !thirdResult.isEmpty()) {
this.graphId = (Integer) thirdResult.get(0).get(0);
}
// find vertex, which has innderId = sid
if (sid >= 0) {
String secondRequest = "select s1.db_id " + "from vertex s1 " + "where s1.db_id_inner_graph = "
+ Integer.valueOf(sid).toString() + ";";
List<List<Object>> secondResult = this.model.executeSQLRequest(secondRequest);
if (secondResult != null && !secondResult.isEmpty()) {
int rid = (Integer) secondResult.get(0).get(0); // result id
StorableVertex rsv = this.model.getStorableVertex(rid); // root storable vertex
// find attribute 'name'
List<StorableAttribute> lsa = rsv.getAttributes();
if (lsa != null) {
for (StorableAttribute bufSA : lsa) {
if (bufSA.equals("name")) {
String value = bufSA.getValue();
if (value != null) {
this.title = bufSA.getValue() + " by name";
}
return;
}
}
}
// other find id of vertex
String buf = rsv.getId();
if (buf != null && buf.length() > 0) {
this.title = buf + " by id";
return;
}
} else {
// it is root
StorableSubGraph ssg = this.model.getStorableSubGraph(sid);
String buf = ssg.getName();
if (buf != null && buf.length() > 0) {
this.title = buf + " by name";
return;
}
buf = ssg.getId();
if (buf != null && buf.length() > 0) {
this.title = buf + " by id";
return;
}
}
}
}
}
this.title = "Unknown";
}
private ArrayList<Object> findPath(final StorableVertex source, final StorableVertex target) {
mxCell from = null;
mxCell to = null;
Object parent = currentGraph.getDefaultParent();
Object[] cells = currentGraph.getChildVertices(parent);
for (Object c : cells) {
if (c instanceof mxCell) {
mxCell cell = (mxCell) c;
if (cell.getId().equals("v" + source.getStorableId())) {
from = cell;
}
if (cell.getId().equals("v" + target.getStorableId())) {
to = cell;
}
}
}
if (from == null || to == null)
return null;
ArrayList<Object> paths = new ArrayList<Object>();
ArrayList<Object> visited = new ArrayList<Object>();
dfs(from, to, paths, visited, null);
paths.add(from);
return paths;
}
private void dfs(final Object root, final Object target, List<Object> paths, List<Object> visited, Object prevEdge) {
visited.add(root);
Object[] e = (subGraph.isDirected()) ? currentGraph.getOutgoingEdges(root) : currentGraph.getConnections(root);
if (e == null)
return;
for (Object edge : e) {
if (edge.equals(prevEdge))
continue;
Object[] opp = currentGraph.getOpposites(new Object[] { edge }, root);
if (opp != null && opp.length > 0) {
Object o = opp[0];
if (o.equals(target)) {
paths.add(o);
paths.add(edge);
} else {
if (visited.contains(o))
continue;
int l = paths.size();
dfs(o, target, paths, visited, edge);
if (l != paths.size()) {
paths.add(edge);
paths.add(o);
}
}
}
}
visited.remove(root);
}
private void turnObservers() {
Object[] arrLocal;
synchronized (this.observers) {
/*
* We don't want the Observer doing callbacks into arbitrary code while holding its own Monitor. The code where we extract each
* Observable from the Vector and store the state of the Observer needs synchronization, but notifying observers does not
* (should not). The worst result of any potential race-condition here is that: 1) a newly-added Observer will miss a
* notification in progress 2) a recently unregistered Observer will be wrongly notified when it doesn't care
*/
arrLocal = this.observers.toArray();
}
for (int i = 0; i < arrLocal.length; i++) {
((Observer) arrLocal[i]).update(null, null);
}
}
private mxIEventListener undoHandle = new mxIEventListener() {
public void invoke(Object sender, mxEventObject evt) {
undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty("edit"));
}
};
private List<StorableEdge> getAllEdgesBeetweenVertexes(List<StorableVertex> vertexes) {
List<StorableEdge> edges = new ArrayList<StorableEdge>();
if (this.subGraph.getEdges() != null) {
for (StorableEdge e : subGraph.getEdges()) {
boolean s = false;
boolean t = false;
for (StorableVertex v : vertexes) {
if (!s) {
s = e.getStorableSource().getStorableId() == v.getStorableId();
}
if (!t) {
t = e.getStorableTarget().getStorableId() == v.getStorableId();
}
if (s && t) {
break;
}
}
if (s && t) {
edges.add(e);
}
}
}
return (edges);
}
// graph converter from Visual Graph format into jgraphx format
private void loadGraph() {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JGraphView.this.loadGraph();
}
});
return;
}
Object parent = currentGraph.getDefaultParent();
List<StorableVertex> graphVertexes = subGraph.getVertices();
List<StorableEdge> graphEdges = subGraph.getEdges();
// loading graph from model
currentGraph.getModel().beginUpdate();
try {
HashMap<StorableVertex, mxCell> v2v = new HashMap<StorableVertex, mxCell>();
subgraphsid = new HashMap<mxCell, Integer>();
// Vertexes load
// TODO add id firstly should be in model
if (graphVertexes != null) {
for (int i = 0; i < graphVertexes.size(); i++) {
mxCell vertex = (mxCell) currentGraph.insertVertex(parent,
"v" + Integer.toString(graphVertexes.get(i).getStorableId()), "", 0, 0, 0, 0);
vertex.setConnectable(false);
currentGraph.updateCellSize(vertex);
v2v.put(graphVertexes.get(i), vertex);
Integer s = graphVertexes.get(i).getInnerGraph();
if (s != null) {
subgraphsid.put(vertex, s);
vertex.setStyle(vertex.getStyle() + ";" + mxConstants.STYLE_FONTSTYLE + "=" + mxConstants.FONT_UNDERLINE + ";"
+ mxConstants.STYLE_FONTCOLOR + "=" + "blue");
}
HashMap<StorableAttribute, Boolean> attrs = new HashMap<StorableAttribute, Boolean>();
for (StorableAttribute attr : graphVertexes.get(i).getAttributes()) {
if (attr.getName().toLowerCase().compareTo("color") == 0)
vertex.setStyle(vertex.getStyle() + ";" + mxConstants.STYLE_FILLCOLOR + "=" + attr.getValue());
if (attr.getName().toLowerCase().compareTo("shape") == 0)
vertex.setStyle(vertex.getStyle() + ";" + mxConstants.STYLE_SHAPE + "=" + attr.getValue());
if (attr.getName().equalsIgnoreCase("name"))
attrs.put(attr, new Boolean(true));
else
attrs.put(attr, new Boolean(false));
}
this.vertexAttributes.put(vertex, attrs);
}
}
// Edges load
boolean direction = subGraph.isDirected();
if (graphEdges != null) {
for (int i = 0; i < graphEdges.size(); i++) {
mxCell edge = (mxCell) currentGraph.insertEdge(parent, "e" + Integer.toString(graphEdges.get(i).getStorableId()), "",
v2v.get(graphEdges.get(i).getStorableSource()), v2v.get(graphEdges.get(i).getStorableTarget()));
if (!direction)
edge.setStyle(edge.getStyle() + ";" + mxConstants.STYLE_ENDARROW + "=" + mxConstants.NONE);
HashMap<StorableAttribute, Boolean> attrs = new HashMap<StorableAttribute, Boolean>();
for (StorableAttribute attr : graphEdges.get(i).getStorableAttributes()) {
if (attr.getName().toLowerCase().compareTo("dashed") == 0)
edge.setStyle(edge.getStyle() + ";" + mxConstants.STYLE_DASHED + "=" + attr.getValue());
if (attr.getName().equalsIgnoreCase("name"))
attrs.put(attr, new Boolean(true));
else
attrs.put(attr, new Boolean(false));
}
this.edgeAttributes.put(edge, attrs);
}
}
} finally {
currentGraph.getModel().endUpdate();
}
// Set default visual attributes----------
HashSet<String> vertexAttr = new HashSet<String>();
HashSet<String> edgeAttr = new HashSet<String>();
vertexAttr.add("name");
edgeAttr.add("name");
showAttributes(vertexAttr, edgeAttr);
// ---------------------------------------
parent = currentGraph.getDefaultParent();
executeLayout();
}
private void guiSetup() {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JGraphView.this.guiSetup();
}
});
return;
}
graphComponent = new mxGraphComponent(currentGraph);
graphComponent.setConnectable(false);
graphComponent.setToolTips(true);
graphComponent.setDragEnabled(false);
graphComponent.setZoomFactor(2);
// allow rubberband selection
new mxRubberband(graphComponent);
// allow use keyboard
new mxKeyboardHandler(graphComponent);
// minimap
graphOutline = new mxGraphOutline(graphComponent);
MouseWheelListener wheelTracker = new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getSource() instanceof mxGraphOutline || e.isControlDown()) {
if (e.getWheelRotation() < 0) {
graphComponent.zoomIn();
} else {
graphComponent.zoomOut();
}
}
}
};
graphOutline.addMouseWheelListener(wheelTracker);
graphComponent.addMouseWheelListener(wheelTracker);
mxIEventListener undoHandler = new mxIEventListener() {
public void invoke(Object sender, mxEventObject evt) {
List<mxUndoableChange> changes = ((mxUndoableEdit) evt.getProperty("edit")).getChanges();
currentGraph.setSelectionCells(currentGraph.getSelectionCellsForChanges(changes));
}
};
mxIEventListener selectHandler = new mxIEventListener() {
public void invoke(Object sender, mxEventObject evt) {
turnObservers();
}
};
mxIEventListener moveHandler = new mxIEventListener() {
public void invoke(Object sender, mxEventObject evt) {
double x = 0, y = 0;
for (Object o : (Object[]) evt.getProperty("cells")) {
mxCell cell = (mxCell) o;
if (cell.getGeometry().getX() < x)
x = cell.getGeometry().getX();
if (cell.getGeometry().getY() < y)
y = cell.getGeometry().getY();
}
if (x != 0 || y != 0)
currentGraph.moveCells(currentGraph.getChildCells(currentGraph.getDefaultParent()), -x, -y);
}
};
undoManager.addListener(mxEvent.UNDO, undoHandler);
undoManager.addListener(mxEvent.REDO, undoHandler);
currentGraph.getSelectionModel().addListener(mxEvent.CHANGE, selectHandler);
currentGraph.addListener(mxEvent.CELLS_MOVED, moveHandler);
this.popup = new JPopupMenu();
final JMenuItem openSubGraphMenuItem = new JMenuItem("Open subgraph in new tab");
openSubGraphMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
IGraphView view = JGraphView.this;
IGraphView subgraph = new JGraphView(JGraphView.this, view.getSelectionSubGraph());
if (!isSelected())
return;
UIRequestOpenSubGraph r = new UIRequestOpenSubGraph(subgraph, JGraphView.this.requestOwner);
JGraphView.this.userInterface.addRequest(r);
}
});
this.popup.add(openSubGraphMenuItem);
this.connectorPopup = new JMenu("go to notepad");
this.popup.add(connectorPopup);
graphComponent.getGraphControl().addMouseListener(new MouseAdapter() {
private void popupTriger(MouseEvent e) {
if (e.isPopupTrigger()) {
openSubGraphMenuItem.setEnabled(JGraphView.this.isSelected());
mxCell cell = (mxCell) graphComponent.getCellAt(e.getX(), e.getY());
if (cell != null) {
connectorPopup.removeAll();
for (final Connection con : connections.values()) {
Set<StorableAttribute> attributes = null;
if (cell.isVertex())
attributes = vertexAttributes.get(cell).keySet();
else if (cell.isEdge()) {
attributes = edgeAttributes.get(cell).keySet();
} else
break;
for (final StorableAttribute attr : attributes) {
if (attr.getName().equals(con.attributeName)) {
JMenuItem item = new JMenuItem(con.connectName);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
UIRequestGoToInAIF r = new UIRequestGoToInAIF(con.connectId, attr.getValue(),
JGraphView.this.requestOwner);
JGraphView.this.userInterface.addRequest(r);
}
});
connectorPopup.add(item);
}
}
}
connectorPopup.setEnabled(connectorPopup.getItemCount() > 0);
} else {
connectorPopup.setEnabled(false);
}
JGraphView.this.popup.show(e.getComponent(), e.getX(), e.getY());
}
SwingUtilities.updateComponentTreeUI(popup);
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.getClickCount() > 1) {
mxCell cell = (mxCell) graphComponent.getCellAt(e.getX(), e.getY());
if (cell != null && subgraphsid.containsKey(cell)) {
if (JGraphView.this.model != null) {
StorableSubGraph ssg = JGraphView.this.model.getStorableSubGraph(subgraphsid.get(cell).intValue());
IGraphView view = new JGraphView(ssg, JGraphView.this.userInterface);
UIRequestOpenSubGraph r = new UIRequestOpenSubGraph(view, JGraphView.this.requestOwner);
JGraphView.this.userInterface.addRequest(r);
} else {
VisualGraph.log.printError("[" + this.getClass().getName()
+ ".JGraphView.mouseReleased] [BAD] Can't get storable subgraph, because model = null.");
}
}
}
popupTriger(e);
}
@Override
public void mousePressed(MouseEvent e) {
popupTriger(e);
}
});
}
protected void finalize() throws Throwable {
JGraphSettings.removeListner(settingListner);
super.finalize();
}
private class Connection {
private int connectId;
private String connectName;
private String attributeName;
}
}