/* * Licensed Materials - Property of Rogue Wave Software, Inc. * © Copyright Rogue Wave Software, Inc. 2014, 2015 * © Copyright IBM Corp. 2009, 2014 * © Copyright ILOG 1996, 2009 * All Rights Reserved. * * Note to U.S. Government Users Restricted Rights: * The Software and Documentation were developed at private expense and * are "Commercial Items" as that term is defined at 48 CFR 2.101, * consisting of "Commercial Computer Software" and * "Commercial Computer Software Documentation", as such terms are * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4, * as applicable. */ import ilog.views.diagrammer.IlvDiagrammer; import ilog.views.diagrammer.application.IlvDiagrammerViewBar; import ilog.views.diagrammer.application.IlvDiagrammerAction; import ilog.views.sdm.IlvSDMEngine; import ilog.views.sdm.renderer.graphlayout.IlvGraphLayoutRenderer; import ilog.views.sdm.util.IlvSDMMutableStyleSheet; import ilog.views.graphlayout.IlvGraphLayout; import ilog.views.graphlayout.IlvGraphLayoutReport; import ilog.views.graphlayout.GraphLayoutEvent; import ilog.views.graphlayout.GraphLayoutEventListener; import ilog.views.graphlayout.random.IlvRandomLayout; import ilog.views.util.IlvProductUtil; import ilog.views.util.swing.IlvSwingUtil; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.net.URL; /** * This is a very simple applet/application that uses the * <code>IlvDiagrammer</code> to perform a hierarchical layout. * It loads a style sheet containing the hierarchical layout specification. * It allows to set various hierarchical layout parameters by using the * <code>IlvSDMMutableStyleSheet</code>. */ public class HierarchicalLayoutApplet extends JApplet { { // This sample uses JViews Diagrammer features. When deploying an // application that includes this code, you need to be in possession // of a Rogue Wave JViews Diagrammer Deployment license. IlvProductUtil.DeploymentLicenseRequired( IlvProductUtil.JViews_Diagrammer_Deployment); } /** The diagrammer */ IlvDiagrammer diagrammer = new IlvDiagrammer(); /** A graph layout event listener */ LayoutIterationListener layoutListener = new LayoutIterationListener(); /** A text field to display messages */ JTextField msgLine = new JTextField(); /** The style sheet for temporary changes */ IlvSDMMutableStyleSheet styleSheet = new IlvSDMMutableStyleSheet( diagrammer.getEngine(), true, false); /** The flow direction selector */ JComboBox flowDirectionSelector = new JComboBox(); /** The link style selector */ JComboBox linkStyleSelector = new JComboBox(); /** Text fields for the offset parameters */ JTextField horiNodeOffset = new JTextField("" + 40.0, 6); JTextField vertNodeOffset = new JTextField("" + 40.0, 6); JTextField horiLinkOffset = new JTextField("" + 15.0, 6); JTextField vertLinkOffset = new JTextField("" + 15.0, 6); JTextField horiNodeLinkOffset = new JTextField("" + 20.0, 6); JTextField vertNodeLinkOffset = new JTextField("" + 20.0, 6); /** * Initializes the applet/application. */ public void init() { // we use the standard diagrammer toolbar IlvDiagrammerViewBar toolbar = new IlvDiagrammerViewBar(); // various settings of the diagrammer diagrammer.setSelectMode(true); diagrammer.setScrollable(true); diagrammer.setEditingAllowed(true); diagrammer.setMinimumZoom(0.02f); diagrammer.setMaximumZoom(10f); diagrammer.getView().setBackground(Color.white); diagrammer.getView().setForeground(SystemColor.windowText); // create the file selector JComboBox fileSelector = new JComboBox(); fileSelector.addItem("small"); fileSelector.addItem("medium"); fileSelector.addItem("large"); fileSelector.addItem("huge"); fileSelector.addItem("ports1"); fileSelector.addItem("ports2"); fileSelector.setToolTipText("Select a sample graph"); fileSelector.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JComboBox comboBox = (JComboBox)event.getSource(); String fileName = (String)comboBox.getSelectedItem(); loadDataFile(fileName); } }); // create the randomize button JButton randomizeButton = new JButton("Random"); randomizeButton.setToolTipText("Randomize the node positions"); randomizeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { randomize(); }}); // create the layout button JButton layoutButton = new JButton("Layout"); layoutButton.setToolTipText("Perform a hierarchical layout"); layoutButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { // before performing layout, make sure that the values in the text // fields are transfered to the style sheet styleSheet.setAdjusting(true); try { setOffset("horizontalNodeOffset", horiNodeOffset); setOffset("verticalNodeOffset", vertNodeOffset); setOffset("horizontalLinkOffset", horiLinkOffset); setOffset("verticalLinkOffset", vertLinkOffset); setOffset("horizontalNodeLinkOffset", horiNodeLinkOffset); setOffset("verticalNodeLinkOffset", vertNodeLinkOffset); } finally { styleSheet.setAdjusting(false); } performLayout(); }}); // create the panel for the layout buttons JPanel layoutButtonPanel = new JPanel(); layoutButtonPanel.setLayout(new FlowLayout()); layoutButtonPanel.add(randomizeButton); layoutButtonPanel.add(layoutButton); // create the top panel with the toolbar and the file selector JPanel topPanel = new JPanel(); topPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); topPanel.add(toolbar); topPanel.add(new JLabel("Select graph:")); topPanel.add(fileSelector); // create the bottom panel with the layout buttons and the message line JPanel bottomPanel = new JPanel(); bottomPanel.setLayout(new BorderLayout()); bottomPanel.add(layoutButtonPanel, BorderLayout.NORTH); bottomPanel.add(createParameterPanel(), BorderLayout.CENTER); bottomPanel.add(msgLine, BorderLayout.SOUTH); msgLine.setEditable(false); // put diagrammer, top panel and bottom panel together getContentPane().setLayout(new BorderLayout(0, 0)); getContentPane().add(diagrammer, BorderLayout.CENTER); getContentPane().add(topPanel, BorderLayout.NORTH); getContentPane().add(bottomPanel, BorderLayout.SOUTH); try { // load the main style file diagrammer.setStyleSheet( IlvSwingUtil.getRelativeURL(this, "data/hierarchical.css")); // load a mutable style file that we use for temporary style changes diagrammer.getEngine().setStyleSheets(1, styleSheet.toString()); } catch (Exception e) { showMessage(e.getMessage()); } // load the initial sample grapher loadDataFile("small"); // add the layout listener to the current graph layout. This must be done // here after reading the style file, because before reading the style file, // the layout instance is not yet instantiated. getGraphLayout().addGraphLayoutEventListener(layoutListener); } /** * Called when this applet is being reclaimed in order to destroy * any resources that it has allocated. */ public void destroy() { super.destroy(); // This method is intended to workaround memory management issues // in the Sun JRE. Please refer to the method documentation for more // details and a description of the known issues. IlvSwingUtil.cleanupApplet(); } /** * Creates the layout parameter panel. */ private JPanel createParameterPanel() { JPanel panel = new JPanel(); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.EAST; panel.setLayout(gridbag); // link style parameter c.gridx = 0; c.gridy = 1; panel.add(new JLabel("Link Style:"), c); c.gridx = 1; panel.add(linkStyleSelector, c); linkStyleSelector.addItem("polyline"); linkStyleSelector.addItem("orthogonal"); linkStyleSelector.addItem("straight"); linkStyleSelector.setSelectedIndex(0); linkStyleSelector.setToolTipText("Select the global link style"); linkStyleSelector.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JComboBox comboBox = (JComboBox)event.getSource(); String style = (String)comboBox.getSelectedItem(); setGlobalLinkStyle(style); } }); // flow direction parameter c.gridx = 0; c.gridy = 2; panel.add(new JLabel("Flow Direction:"), c); c.gridx = 1; panel.add(flowDirectionSelector, c); flowDirectionSelector.addItem("top"); flowDirectionSelector.addItem("bottom"); flowDirectionSelector.addItem("left"); flowDirectionSelector.addItem("right"); flowDirectionSelector.setSelectedIndex(3); flowDirectionSelector.setToolTipText("Select the link flow direction"); flowDirectionSelector.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JComboBox comboBox = (JComboBox)event.getSource(); String flow = (String)comboBox.getSelectedItem(); setFlowDirection(flow); } }); // hierarchical layout offset parameters c.gridx = 3; c.gridy = 0; panel.add(new JLabel("horizontal"), c); c.gridy = 1; panel.add(horiNodeOffset, c); c.gridy = 2; panel.add(horiNodeLinkOffset, c); c.gridy = 3; panel.add(horiLinkOffset, c); c.gridx = 4; c.gridy = 0; c.insets = new Insets(0, 5, 0, 0); panel.add(new JLabel("vertical"), c); c.gridy = 1; panel.add(vertNodeOffset, c); c.gridy = 2; panel.add(vertNodeLinkOffset, c); c.gridy = 3; panel.add(vertLinkOffset, c); c.gridx = 2; c.gridy = 0; c.insets = new Insets(0, 25, 0, 0); panel.add(new JLabel("Offsets"), c); c.gridy = 1; panel.add(new JLabel("bw. nodes:"), c); c.gridy = 2; panel.add(new JLabel("bw. node/link:"), c); c.gridy = 3; panel.add(new JLabel("bw. link bends:"), c); horiNodeOffset.setToolTipText( "Set the minimal horizontal offset between nodes"); horiNodeOffset.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setOffset("horizontalNodeOffset", (JTextField)e.getSource()); } }); horiNodeLinkOffset.setToolTipText( "Set the minimal horizontal offset between nodes and links"); horiNodeLinkOffset.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setOffset("horizontalNodeLinkOffset", (JTextField)e.getSource()); } }); horiLinkOffset.setToolTipText( "Set the minimal horizontal offset between link bends"); horiLinkOffset.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setOffset("horizontalLinkOffset", (JTextField)e.getSource()); } }); vertNodeOffset.setToolTipText( "Set the minimal vertical offset between nodes"); vertNodeOffset.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setOffset("verticalNodeOffset", (JTextField)e.getSource()); } }); vertNodeLinkOffset.setToolTipText( "Set the minimal vertical offset between nodes and links"); vertNodeLinkOffset.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setOffset("verticalNodeLinkOffset", (JTextField)e.getSource()); } }); vertLinkOffset.setToolTipText( "Set the minimal vertical offset between link bends"); vertLinkOffset.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setOffset("verticalLinkOffset", (JTextField)e.getSource()); } }); return panel; } /** * Load a sample data file. * @param fileNameBase The base of the filename, excluding the path prefix * and the extension suffix. */ private void loadDataFile(String fileNameBase) { try { showMessage("Reading " + fileNameBase + ".xml ..."); diagrammer.setDataFile( IlvSwingUtil.getRelativeURL(this, "data/" + fileNameBase + ".xml")); showMessage("Reading " + fileNameBase + ".xml done."); prepare(fileNameBase); } catch (Exception e) { showMessage(e.getMessage()); } } /** * Just for demo purpose: Select the orthogonal link style for the * port samples, since the look better this way. */ private void prepare(String fileNameBase) { if (fileNameBase.equals("medium") || fileNameBase.equals("ports1") || fileNameBase.equals("ports2")) { linkStyleSelector.setSelectedIndex(1); setGlobalLinkStyle("orthogonal"); } } /** * Sets the flow direction. */ private void setFlowDirection(String flow) { if (flow.equals("top")) styleSheet.setDeclaration("GraphLayout", "flowDirection", "Top"); else if (flow.equals("bottom")) styleSheet.setDeclaration("GraphLayout", "flowDirection", "Bottom"); else if (flow.equals("left")) styleSheet.setDeclaration("GraphLayout", "flowDirection", "Left"); else if (flow.equals("right")) styleSheet.setDeclaration("GraphLayout", "flowDirection", "Right"); } /** * Sets the global link style. */ private void setGlobalLinkStyle(String style) { if (style.equals("orthogonal")) styleSheet.setDeclaration("GraphLayout", "globalLinkStyle", "ORTHOGONAL_STYLE"); else if (style.equals("straight")) styleSheet.setDeclaration("GraphLayout", "globalLinkStyle", "STRAIGHT_LINE_STYLE"); else if (style.equals("polyline")) styleSheet.setDeclaration("GraphLayout", "globalLinkStyle", "POLYLINE_STYLE"); } /** * Sets the node, node/link or link offset from a textfield. */ private void setOffset(String property, JTextField valueSelector) { try { // get the value of the parameter from the valueSelector float value = Float.valueOf(valueSelector.getText().trim()).floatValue(); // write again to be sure the right value is shown valueSelector.setText("" + value); // set the new value of the layout property styleSheet.setDeclaration("GraphLayout", property, "" + value); } catch (Exception ex) { showMessage("Illegal " + property + ": " + ex.getMessage()); } } /** * Performs the hierarchical layout. * The layout type is stored in the CSS file, therefore we don't need to * set the hierarchical layout explicitely. After loading the graph and * the CSS style sheet, the hierarchical layout is already instantiated. */ private void performLayout() { showMessage("Layout started..."); // initialize the iteration listener layoutListener.initialize(getGraphLayout()); // Perform Hierarchical Layout as node layout. Hierarchical layout always // handles nodes and links at the same time, therefore a separate link // layout is not necessary. if (diagrammer.isNodeLayoutAvailable()) { // perform the layout diagrammer.layoutAllNodes(); // show the layout report information about the return code IlvGraphLayoutReport report = getGraphLayout().getLayoutReport(); if (report != null) { showMessage(report.codeToString(report.getCode()) + "."); // if layout was successfully done, fit in the diagrammer's view if (report.getCode() == IlvGraphLayoutReport.LAYOUT_DONE) { diagrammer.fitToContents(); } } } } /** * Randomize the node positions. */ private void randomize() { // clear previous message showMessage(""); // create a new random layout IlvRandomLayout randomLayout = new IlvRandomLayout(); // save the old layout instance IlvGraphLayout oldLayout = getGraphLayout(); // tell the diagrammer to temporarily use the random layout, which // disables the hierarchical layout. setGraphLayout(randomLayout); if (diagrammer.isNodeLayoutAvailable()) { // perform the layout diagrammer.layoutAllNodes(); } // restore the old layout instance setGraphLayout(oldLayout); } /** * Returns the graph layout currently used by the diagrammer. */ private IlvGraphLayout getGraphLayout() { IlvGraphLayoutRenderer renderer = (IlvGraphLayoutRenderer)diagrammer.getEngine().getNodeLayoutRenderer(); return renderer == null ? null : renderer.getGraphLayout(); } /** * Sets the graph layout currently used by the diagrammer. */ private void setGraphLayout(IlvGraphLayout layout) { IlvGraphLayoutRenderer renderer = (IlvGraphLayoutRenderer)diagrammer.getEngine().getNodeLayoutRenderer(); if (renderer == null) return; renderer.setGraphLayout(layout); } /** * Displays a message. */ void showMessage(String message) { // the message is displayed in the message line msgLine.setText(message); msgLine.paintImmediately(0, 0, msgLine.getWidth(), msgLine.getHeight()); } /** * Allows you to run the demo as a standalone application. */ public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { HierarchicalLayoutApplet applet = new HierarchicalLayoutApplet(); applet.init(); JFrame frame = new JFrame("Hierarchical Layout Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(600, 600); frame.getContentPane().add(applet); frame.setVisible(true); } }); } // -------------------------------------------------------------------------- /** * A graph layout iteration listener. Implementing the interface * GraphLayoutEventListener gives you the possibility to receive * during the layout information about the behavior of the layout * algorithm. This information is contained in the graph layout event. * * In our case, we will simply print the percentage completed by the * layout each time the method layoutStepPerformed is called, that is * after each iteration of the Hierarchical Layout algorithm. */ class LayoutIterationListener implements GraphLayoutEventListener { IlvGraphLayout layout; private float oldPercentage; /** * This method is automatically called by the layout algorithm. */ public void layoutStepPerformed(GraphLayoutEvent event) { float percentage = event.getLayoutReport().getPercentageComplete(); if (percentage != oldPercentage) { showMessage("" + percentage + "%"); oldPercentage = percentage; } } /** * Initialize the listener. * This method must be called before the layout is started. */ void initialize(IlvGraphLayout l) { if (layout != l) { if (layout != null) layout.removeGraphLayoutEventListener(this); layout = l; if (layout != null) layout.addGraphLayoutEventListener(this); } oldPercentage = -1.0f; } } }