Another day with JGraph – Styles and Constrained Children

Today I spent some more time playing with JGraph. The application I am creating requires three basic shapes: a rectangle, an ellipse and a roundtangle (rounded rectangle). All of these shapes are supported in JGraph using the styles mechanism, descibed here in the user manual. A very useful class that must be read and re-read for creating styles is mxConstants which contains many definitions for the style customizations and their possible values.
For example, creating a style for an ellipse looks like this (using a bluish color):

Hashtable<String, Object> style = new Hashtable<String, Object>();
style.put(mxConstants.STYLE_FILLCOLOR, mxUtils.getHexColorString(Color.WHITE));
style.put(mxConstants.STYLE_STROKEWIDTH, 1.5);
style.put(mxConstants.STYLE_STROKECOLOR, mxUtils.getHexColorString(new Color(0, 0, 170)));
style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
style.put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE);

and the style for a rountangle looks like this (using a brownish color):

Hashtable<String, Object> style = new Hashtable<String, Object>();
style.put(mxConstants.STYLE_FILLCOLOR, mxUtils.getHexColorString(Color.WHITE));
style.put(mxConstants.STYLE_STROKEWIDTH, 1.5);
style.put(mxConstants.STYLE_STROKECOLOR, mxUtils.getHexColorString(new Color(0, 0, 170)));
style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
style.put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE);

After the style is created it must be added to the graph’s stylesheet. The following adds a style created above giving it the name MyStyle, using it to create a new vertex using this style:

mxGraph graph = new mxGraph();
mxStylesheet stylesheet = graph.getStylesheet();
stylesheet.putCellStyle("MyStyle", style);
graph.insertVertex(defaultParent, null, "Hello", 20, 20, 80, 30, "MyStyle");

An interesting style that I had to add to the styles was the mxConstants.STYLE_PERIMETER style. What happened is that after creating an ellipse style and adding a link starting at a vertex with this style, the link did not start at the border of the ellipse but some space outside, in the bounding rectangle of the ellipse. This style setting fixes this.
Another nice feature in JGraph is the hierarchical definition of the graph, where all entities in the graph (called cells in JGraph) can have a parent that contains it. This is a feature that I definitely need, and is easy to use. The first parameter in the insertVertex and insertEdge functions of the mxGraph defines the parent of the inserted node. In my previous post, I used a defaultParent when creating new cells. Now I use an existing cell as the parent, using the following code:

Object v1 = graph.insertVertex(defaultParent, null, "Process", 20, 20, 80, 30, "process");
Object v2 = graph.insertVertex(defaultParent, null, "Object", 240, 150, 80, 60, "object");
Object v3 = graph.insertVertex(v2, null, "State", 10, 35, 30, 20, "state");

And creating the following graph:

In the default JGraph implementation, the inner vertex can be moved outside the boundaries of the containing vertex, which is not something that is allowed in my application. Reading the mxGraph javadoc some possible functions seemed to make this happen: setConstrainChildren and setDefaultOverlap, but they seemed to do nothing. A short search in the internet produced this solution, where the mxGraphComponent is extended as follows (since I never allow for drag-and-drop outside a parent):

mxGraphComponent graphComponent = new mxGraphComponent(graph) {
	private static final long serialVersionUID = 1821677322838455152L;
	@Override public mxGraphHandler createGraphHandler() {
		return new mxGraphHandler(this) {
			@Override protected boolean shouldRemoveCellFromParent(Object parent, Object[] cells, MouseEvent e) {
				return false;
			}
		};
	}
}; 

It will be interesting to investigate what previously mentioned function do, but the solution works. Long live programming by coincidence. The full code of the developed example is shown below. See you next time.

package com.vainolo.jgraph;

import java.awt.Color;
import java.awt.event.MouseEvent;
import java.util.Hashtable;
import javax.swing.JFrame;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.handler.mxGraphHandler;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxStylesheet;

/**
 * Second JGraph self-tutorial.
 * This class creates a three-node graph, with two first class vertex and one
 * vertex that is child of a first class vertex.
 * Styles are implemented for each one of the vertices.
 * The {@link mxGraphComponent} class is extended so that the inner vertex cannot be
 * dragged outside the parent vertex.
 * 
 * @author vainolo
 *
 */
public class JGraphLearning2 extends JFrame {
	private static final long serialVersionUID = -3363276407804479930L;
	/**
	 * Create all required styles.
	 * Create a three node graph with an inner node.
	 * Extend the {@link mxGraphComponent} so that inner vertex cannot be dragged
	 * outside their parent.
	 */
	public JGraphLearning2() {
		super("JGraphXLearning2");
		mxGraph graph = new mxGraph();
		configureGraph(graph);
		Object defaultParent = graph.getDefaultParent();
		mxStylesheet stylesheet = graph.getStylesheet();
		stylesheet.putCellStyle("process", createProcessStyle());
		stylesheet.putCellStyle("object", createObjectStyle());
		stylesheet.putCellStyle("state", createStateStyle());
		stylesheet.putCellStyle("agent", createAgentLinkStyle());
		graph.getModel().beginUpdate();
		Object v1 = graph.insertVertex(defaultParent, null, "Process", 20, 20, 80, 30, "process");
		Object v2 = graph.insertVertex(defaultParent, null, "Object", 240, 150, 80, 60, "object");
		Object v3 = graph.insertVertex(v2, null, "State", 10, 35, 30, 20, "state");
		graph.insertEdge(defaultParent, null, "", v1, v2, "agent");
		graph.getModel().endUpdate();
		mxGraphComponent graphComponent = new mxGraphComponent(graph) {
			private static final long serialVersionUID = 1821677322838455152L;
			@Override public mxGraphHandler createGraphHandler() {
				return new mxGraphHandler(this) {
					@Override protected boolean shouldRemoveCellFromParent(Object parent, Object[] cells, MouseEvent e) {
						return false;
					}
					
				};
			}
		}; 
		configureGraphComponent(graphComponent);
		getContentPane().add(graphComponent);
	}
	
	/**
	 * General graph settings.
	 * @param graph the graph to configure.
	 */
	private void configureGraph(mxGraph graph) {
		graph.setEnabled(false);
		graph.setCellsResizable(true);
		graph.setConstrainChildren(true);
		graph.setExtendParents(true);
		graph.setExtendParentsOnAdd(true);
		graph.setDefaultOverlap(0);
	}
	
	/**
	 * General graph component settings.
	 * @param graphComponent
	 */
	private void configureGraphComponent(mxGraphComponent graphComponent) {
		graphComponent.getViewport().setOpaque(true);
		graphComponent.getViewport().setBackground(Color.WHITE);
		graphComponent.setConnectable(false);
	}	
	
	/**
	 * Create a new style for process vertices.
	 * @return the created style.
	 */
	private Hashtable<String,Object> createProcessStyle() {
		Hashtable<String, Object> style = new Hashtable<String, Object>();
		style.put(mxConstants.STYLE_FILLCOLOR, mxUtils.getHexColorString(Color.WHITE));
		style.put(mxConstants.STYLE_STROKEWIDTH, 1.5);
		style.put(mxConstants.STYLE_STROKECOLOR, mxUtils.getHexColorString(new Color(0, 0, 170)));
		style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
		style.put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE);
		return style;
	}
	
	/**
	 * Create a new style for object vertices.
	 * @return the created style.
	 */
	private Hashtable<String, Object> createObjectStyle() {
		Hashtable<String, Object> style = new Hashtable<String, Object>();
		style.put(mxConstants.STYLE_FILLCOLOR, mxUtils.getHexColorString(Color.WHITE));
		style.put(mxConstants.STYLE_STROKECOLOR, mxUtils.getHexColorString(new Color(0, 110, 0)));
		style.put(mxConstants.STYLE_STROKEWIDTH, 1.5);
		style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_RECTANGLE);
		style.put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_RECTANGLE);
		return style;
	}
	
	/**
	 * Create a new style for state vertices.
	 * @return the created style.
	 */
	private Hashtable<String, Object> createStateStyle() {
		Hashtable<String, Object> style = new Hashtable<String, Object>();
		style.put(mxConstants.STYLE_FILLCOLOR, mxUtils.getHexColorString(Color.WHITE));
		style.put(mxConstants.STYLE_STROKECOLOR, mxUtils.getHexColorString(new Color(91, 91, 0)));
		style.put(mxConstants.STYLE_STROKEWIDTH, 1.5);		
		style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_RECTANGLE);
		style.put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_RECTANGLE);
		style.put(mxConstants.STYLE_ROUNDED, true);
		return style;
	}

	/**
	 * Create a new style for agent links.
	 * @return the created style.
	 */
	private Hashtable<String, Object> createAgentLinkStyle() {
		Hashtable<String, Object> style = new Hashtable<String, Object>();
		style.put(mxConstants.STYLE_STROKECOLOR, mxUtils.getHexColorString(Color.BLACK));		
		style.put(mxConstants.STYLE_ENDARROW, mxConstants.ARROW_OVAL);
		return style;
	}
	
	/**
	 * Execute the program.
	 * @param args ignored.
	 */
	public static void main(String args[]) {
		JGraphLearning2 frame = new JGraphLearning2();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(400, 320);
		frame.setVisible(true);
	}
}

10 thoughts on “Another day with JGraph – Styles and Constrained Children

  1. Hi,

    I have a Problem and maybe you can help me.
    I studie ComputerScience and i have to program a GUI with JGraph(It’s a Project). This GUI is for a Self Programmed Simulation tool (I did’nt program the tool). And this simulation already got a GUI but this one is programmed with the JUNG Library. Now my Question: You programmed with both Library’s and i need to find a Component or something else in JGraph with equals the VisualizationViewer ind JUNG. Maybe you could help me.

    Greetings
    Daniel

    • Hi Daniel.
      The Visualisation Viewer in JUNG is the graphical component that is added to the GUI of your project. In JGraph, this would be a mxGraphComponent. But you cannot use JGraph views with JUNG models. You will probably need to rewrite a lot of code for this.
      Good Luck

      Arieh

  2. Hi,

    Maybe you could answer my Comment before to my E-Mail address.

    greetings
    Daniel

    PS:
    In the comment before the should stand wich equals and not with equals ;)

  3. Hi,

    Yeah i program all stuff new which is programmed in Jung. I want to port all Jung stuff to JGraph.

    I really thank you for your answer.
    You really helped me out.

    Daniel

  4. Hi,
    i got an another Problem too.
    I Create a FastOrganicLayout and in Jung the Layout is the parameter for the VizualisationViewer but in JGraph for the mxGraphComponent the parameter is an mxGraph and not an mxLayout.
    I supposed that the Layout is assigned to the Graph automatically because in the constructor i create the Layout for a mxGraph.
    This shouldn’t be a Problem.

  5. Hi,

    When you want to work with the JGraph library u add the JGraph library to your Buildpath or?
    I did this but if i start my Program i get a ClassDefNotFoundError .
    Must i set the EnvironmentVariables for JGraph or should i Copy something from the JGraph folder to my Folder where i use the library?

    I hope you can help me.
    Thank you

    • Obviously you must add the JGraph library to your classpath. If are trying to program something in Java you should already know this.
      If you are new to java, there are many java tutorials which you can read, for example The Java Tutorial by Oracle. I you have specific JGraph or JUNG questions that I can answer I will be glad to help, but not general java questions.
      Good luck.

  6. How in fact does one use the various layouts with JGraph? After numerous hours of Googling I certainly am aware that people use the mxOrganicLayout, a tree layout, etc. However, I am not finding examples of how one uses them. In my application I have created an mxGraph (though I have coordinates on each of my vertices since the insertVertex() requires them) and would like to have an automatic layout (where I don’t have to enter coordinates) but I can’t seem to get it working. Essentially I am taking my mxGraph and passing that to an mxGraphComponent that I can insert into a JPanel. Any recommendations on how I can use an mxOrganicLayout? Thanks.

Leave a Reply