/* * Licensed Materials - Property of Perforce Software, Inc. * © Copyright Perforce Software, Inc. 2014, 2021 * © 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 java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.SystemColor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JRootPane; import javax.swing.JTextField; import ilog.views.IlvGrapher; import ilog.views.IlvGraphic; import ilog.views.IlvGraphicBag; import ilog.views.IlvGraphicEnumeration; import ilog.views.IlvLinkImage; import ilog.views.IlvManager; import ilog.views.IlvObjectWithSelection; import ilog.views.diagrammer.IlvDiagrammer; import ilog.views.diagrammer.application.IlvDiagrammerViewBar; import ilog.views.graphic.linkbundle.IlvLinkBundle; import ilog.views.graphlayout.IlvGraphModel; import ilog.views.graphlayout.IlvGrapherAdapter; import ilog.views.graphlayout.IlvInappropriateLinkException; import ilog.views.sdm.util.IlvSDMMutableStyleSheet; import ilog.views.swing.IlvPopupMenuContext; import ilog.views.swing.IlvPopupMenuManager; import ilog.views.swing.IlvSimplePopupMenu; import ilog.views.util.IlvProductUtil; import ilog.views.util.swing.IlvSwingUtil; /** * This is a very simple application that uses the * <code>IlvDiagrammer</code> to perform a link layout on bundles of links. It * loads a style sheet containing the link bundle specification. It allows you * to set various link layout parameters by using the * <code>IlvSDMMutableStyleSheet</code>. */ public class LinkBundleApp extends JRootPane { { // This sample uses JViews Diagrammer features. When deploying an // application that includes this code, you need // a Perforce JViews Diagrammer Deployment license. IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_Diagrammer_Deployment); } /** The diagrammer */ IlvDiagrammer diagrammer = new IlvDiagrammer(); /** The style sheet for temporary changes */ IlvSDMMutableStyleSheet styleSheet = new IlvSDMMutableStyleSheet(diagrammer.getEngine(), true, false); /** Whether bundles are currently expanded */ boolean expandedState = true; /** * Initializes the 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.02); diagrammer.setMaximumZoom(10); diagrammer.getView().setBackground(Color.white); diagrammer.getView().setForeground(SystemColor.windowText); // for the pop-up menu // register view view to the pop-up manager IlvPopupMenuManager.registerView(diagrammer.getView()); // register the menus under different names IlvPopupMenuManager.registerMenu("COLLAPSE_BUNDLE", createCollapseMenu()); IlvPopupMenuManager.registerMenu("EXPAND_BUNDLE", createExpandMenu()); // create the file selector JComboBox<String> cssSelector = new JComboBox<String>(); cssSelector.addItem("Composite links"); cssSelector.addItem("Simple links"); cssSelector.addItem("Nested bundles"); cssSelector.setToolTipText("Select a sample graph"); cssSelector.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { SuppressWarnings("unchecked") JComboBox<String> comboBox = (JComboBox<String>) event.getSource(); String cssName = (String) comboBox.getSelectedItem(); if ("Composite links".equals(cssName)) cssName = "composite"; else if ("Simple links".equals(cssName)) cssName = "flat"; else if ("Nested bundles".equals(cssName)) cssName = "nested"; loadCSS(cssName); } }); // create the layout button JButton layoutButton = new JButton("Layout"); layoutButton.setToolTipText("Perform a link layout"); layoutButton.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent evt) { } }); // 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 CSS:")); topPanel.add(cssSelector); // create the bottom panel with the further options JPanel bottomPanel = new JPanel(); JPanel bottomPanel1 = new JPanel(); JPanel bottomPanel2 = new JPanel(); bottomPanel.setLayout(new GridLayout(2, 0)); bottomPanel1.setLayout(new FlowLayout(FlowLayout.LEFT)); bottomPanel2.setLayout(new FlowLayout(FlowLayout.LEFT)); bottomPanel.add(bottomPanel1); bottomPanel.add(bottomPanel2); addFurtherOptions(bottomPanel1, bottomPanel2); // 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); // prepare the mutual style sheet styleSheet.setDeclaration("link", "singleConnectionPoint", "false"); styleSheet.setDeclaration("Subobject#LinkBundleA", "singleConnectionPoint", "false"); styleSheet.setDeclaration("Subobject#LinkBundleB", "singleConnectionPoint", "false"); styleSheet.setDeclaration("link", "offset", "5"); styleSheet.setDeclaration("Subobject#LinkBundleA", "offset", "5"); styleSheet.setDeclaration("Subobject#LinkBundleB", "offset", "5"); styleSheet.setDeclaration("link", "frame", "+BundleFrame"); styleSheet.setDeclaration("Subobject#LinkBundleA", "frame", "+BundleFrameA"); styleSheet.setDeclaration("Subobject#LinkBundleB", "frame", "+BundleFrameB"); styleSheet.setDeclaration("Subobject#BundleFrame", "margin", "3"); styleSheet.setDeclaration("Subobject#BundleFrameA", "margin", "3"); styleSheet.setDeclaration("Subobject#BundleFrameB", "margin", "3"); styleSheet.setDeclaration("link", "collapsed", "false"); // load the CSS file loadCSS("composite"); // load the initial sample grapher loadDataFile("sample1"); } /** * Fill the bottom panels with further options. */ private void addFurtherOptions(JPanel bottomPanel1, JPanel bottomPanel2) { // ---------------------------------------- // Link style bottomPanel1.add(new JLabel("Link Style:")); JComboBox<String> linkStyleChooser = new JComboBox<String>(); bottomPanel1.add(linkStyleChooser); linkStyleChooser.setToolTipText("Select the link shape"); linkStyleChooser.addItem("Orthogonal"); linkStyleChooser.addItem("Direct"); linkStyleChooser.addItem("Straight"); linkStyleChooser.addItemListener(new ItemListener() { Override public void itemStateChanged(ItemEvent event) { if (event.getStateChange() == ItemEvent.DESELECTED) return; String styleName = (String) event.getItem(); if (styleName.equals("Orthogonal")) setLinkStyle("ORTHOGONAL_STYLE"); else if (styleName.equals("Direct")) setLinkStyle("DIRECT_STYLE"); else if (styleName.equals("Straight")) setLinkStyle("STRAIGHT_STYLE"); else throw new RuntimeException("Unsupported link style: " + styleName); } }); // ---------------------------------------- // Single connection point JCheckBox singleConnectionPointCheckbox = new JCheckBox("Single connection point", false); bottomPanel1.add(singleConnectionPointCheckbox); singleConnectionPointCheckbox.setToolTipText("Enables the single connection point mode"); singleConnectionPointCheckbox.addItemListener(new ItemListener() { Override public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { styleSheet.setDeclaration("link", "singleConnectionPoint", "true"); styleSheet.setDeclaration("Subobject#LinkBundleA", "singleConnectionPoint", "true"); styleSheet.setDeclaration("Subobject#LinkBundleB", "singleConnectionPoint", "true"); } else if (e.getStateChange() == ItemEvent.DESELECTED) { styleSheet.setDeclaration("link", "singleConnectionPoint", "false"); styleSheet.setDeclaration("Subobject#LinkBundleA", "singleConnectionPoint", "false"); styleSheet.setDeclaration("Subobject#LinkBundleB", "singleConnectionPoint", "false"); } } }); // -------------------------------------------- // Offset JLabel offsetLabel = new JLabel("Offset:"); String tooltip = "Sets the offset between the sublinks of the bundles."; offsetLabel.setToolTipText(tooltip); bottomPanel1.add(offsetLabel); final JTextField offsetField = new JTextField("5.0 "); offsetField.setToolTipText(tooltip); bottomPanel1.add(offsetField); offsetField.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { float value = Float.valueOf(offsetField.getText().trim()).floatValue(); offsetField.setText("" + value); styleSheet.setDeclaration("link", "offset", "" + value); styleSheet.setDeclaration("Subobject#LinkBundleA", "offset", "" + value); styleSheet.setDeclaration("Subobject#LinkBundleB", "offset", "" + value); } }); // ----------------------------------------------- // Link Bundle Frame final JPanel bundleFramePanel = new JComfortPanel(); final JLabel marginLabel = new JLabel("Frame margin:"); tooltip = "Sets the margin"; marginLabel.setToolTipText(tooltip); final JTextField marginField = new JTextField("3.0 "); marginField.setToolTipText(tooltip); marginField.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { float value = Float.valueOf(marginField.getText().trim()).floatValue(); marginField.setText("" + value); styleSheet.setDeclaration("Subobject#BundleFrame", "margin", "" + value); styleSheet.setDeclaration("Subobject#BundleFrameA", "margin", "" + value); styleSheet.setDeclaration("Subobject#BundleFrameB", "margin", "" + value); } }); final JCheckBox bundleFrameCheckbox = new JCheckBox("Bundle frame", true); bundleFrameCheckbox.setToolTipText("Enables the use of a frame around the link bundles"); bundleFrameCheckbox.addItemListener(new ItemListener() { Override public void itemStateChanged(ItemEvent e) { // specify the new link style option if (e.getStateChange() == ItemEvent.SELECTED) { styleSheet.setDeclaration("link", "frame", "+BundleFrame"); styleSheet.setDeclaration("Subobject#LinkBundleA", "frame", "+BundleFrameA"); styleSheet.setDeclaration("Subobject#LinkBundleB", "frame", "+BundleFrameB"); marginLabel.setEnabled(true); marginField.setEnabled(true); } else if (e.getStateChange() == ItemEvent.DESELECTED) { styleSheet.setDeclaration("link", "frame", ""); styleSheet.setDeclaration("Subobject#LinkBundleA", "frame", ""); styleSheet.setDeclaration("Subobject#LinkBundleB", "frame", ""); marginLabel.setEnabled(false); marginField.setEnabled(false); } } }); bundleFramePanel.add(bundleFrameCheckbox); bundleFramePanel.add(marginLabel); bundleFramePanel.add(marginField); bottomPanel2.add(bundleFramePanel); // ---------------------------------------- // Expand/collapse button final JButton expandCollapseButton = new JButton("Collapse"); bottomPanel2.add(expandCollapseButton); refreshExpandCollapseButton(expandCollapseButton, expandedState); expandCollapseButton.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent evt) { expandedState = !expandedState; refreshExpandCollapseButton(expandCollapseButton, expandedState); bundleFramePanel.setEnabled(expandedState); if (expandedState && !bundleFrameCheckbox.isSelected()) { marginLabel.setEnabled(false); marginField.setEnabled(false); } styleSheet.setAdjusting(true); try { if (expandedState) styleSheet.setDeclaration("link", "collapsed", "false"); else styleSheet.setDeclaration("link", "collapsed", "true"); // remove all declatation about individual expand or collapse. // This is only needed because the pop-up menus introduce such // declarations List<String> individualLinkSelectors = new ArrayList<String>(); Iterator<?> iter = styleSheet.getRuleSelectors(); while (iter.hasNext()) { String selector = (String) iter.next(); // need to collect selectors first to avoid concurrent modifications if (selector.startsWith("#link")) individualLinkSelectors.add(selector); } Iterator<String> iter2 = individualLinkSelectors.iterator(); while (iter2.hasNext()) { String selector = iter2.next(); styleSheet.removeDeclaration(selector, "collapsed"); } } finally { styleSheet.setAdjusting(false); } } }); } /** * Refreshes the button for expanding/collapsing link bundles. */ private void refreshExpandCollapseButton(JButton button, boolean expand) { button.setText(expand ? "Collapse" : "Expand"); button.setToolTipText(expand ? "Collapse the link bundles to show the overview link" : "Expand the link bundles to show the sublinks"); } /** * 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 { diagrammer.setDataFile(IlvSwingUtil.getRelativeURL(this, "data/" + fileNameBase + ".xml")); diagrammer.fitToContents(); } catch (Exception e) { } } /** * Load a CSS file. * * @param fileNameBase * The base of the filename, excluding the path prefix and the * extension suffix. */ private void loadCSS(String fileNameBase) { try { // load the main style file diagrammer.setStyleSheet(IlvSwingUtil.getRelativeURL(this, "data/" + fileNameBase + ".css")); // load a mutable style file that we use for temporary style changes diagrammer.getEngine().setStyleSheets(1, styleSheet.toString()); } catch (Exception e) { } } /** * Sets the orithogonal link style or direct link style. */ private void setLinkStyle(String style) { if (style.equals("STRAIGHT_STYLE")) { styleSheet.setDeclaration("LinkLayout", "enabled", "false"); deleteIntermediateLinkPoints(false); } else { styleSheet.setDeclaration("LinkLayout", "enabled", "true"); styleSheet.setDeclaration("LinkLayout", "globalLinkStyle", style); } } private void deleteIntermediateLinkPoints(boolean redraw) { // remove all intermediate points from all links IlvGrapher grapher = diagrammer.getEngine().getGrapher(); IlvGraphModel graphModel = new IlvGrapherAdapter(grapher); IlvGraphicEnumeration e = grapher.getObjects(); IlvGraphic g; while (e.hasMoreElements()) { g = e.nextElement(); if (grapher.isLink(g)) { IlvLinkImage link = (IlvLinkImage) g; try { graphModel.reshapeLink(link, null, null, 0, 0, null, redraw); } catch (IlvInappropriateLinkException ex) { ex.printStackTrace(); } } } } private JPopupMenu createCollapseMenu() { return createMenu(true); } private JPopupMenu createExpandMenu() { return createMenu(false); } private JPopupMenu createMenu(boolean collapse) { // create the action listener for the pop-up menu ActionListener actionListener = new ActionListener() { Override public void actionPerformed(ActionEvent e) { // retrieve the selected menu item JMenuItem m = (JMenuItem) e.getSource(); // retrieve the graphic that has this pop-up menu IlvPopupMenuContext context = IlvPopupMenuManager.getPopupMenuContext(m); IlvGraphic graphic = context.getGraphic(); // do the action if (m.getText().equals("Collapse")) { if (graphic instanceof IlvLinkBundle) { IlvLinkBundle bundle = (IlvLinkBundle) graphic; collapse(bundle, true); } } if (m.getText().equals("Expand")) { // the expand menu is on the overview link if (graphic.getGraphicBag() instanceof IlvLinkBundle) { IlvLinkBundle ownerBundle = (IlvLinkBundle) graphic.getGraphicBag(); if (ownerBundle.getOverviewLink() == graphic) { graphic = ownerBundle; } } if (graphic instanceof IlvLinkBundle) { IlvLinkBundle bundle = (IlvLinkBundle) graphic; collapse(bundle, false); } } } }; IlvSimplePopupMenu menu = new IlvSimplePopupMenu("Menu1", collapse ? "Collapse" : "Expand", null, actionListener) { // configure checkbox of the pop-up before it gets displayed Override protected void beforeDisplay(IlvPopupMenuContext context) { // always call super.beforeDisplay first super.beforeDisplay(context); // retrieve the graphic IlvGraphic graphic = context.getGraphic(); // deselect all objects in the manager IlvGraphicBag bag = graphic.getGraphicBag(); while (bag != null && !(bag instanceof IlvManager)) { bag = ((IlvGraphic) bag).getGraphicBag(); } if (bag instanceof IlvManager) ((IlvManager) bag).deSelectAll(true, true); // select this graphic; bag = graphic.getGraphicBag(); if (bag instanceof IlvLinkBundle) { IlvLinkBundle ownerBundle = (IlvLinkBundle) graphic.getGraphicBag(); if (ownerBundle.getOverviewLink() == graphic) { graphic = ownerBundle; bag = graphic.getGraphicBag(); } } if (bag instanceof IlvObjectWithSelection) { ((IlvObjectWithSelection) bag).setSelected(graphic, true, true); } } }; return menu; } /** * Collapse or expand an individual link by adding a rule to the mutable style * sheet about the specific link. This works only for the top level bundles, * not for nested bundles. */ private void collapse(IlvLinkBundle link, boolean collapse) { // only top level bundles can have a collapse/expand menu in SDM Object linkModelObj = diagrammer.getEngine().getObject(link); if (linkModelObj == null) return; String id = diagrammer.getID(linkModelObj); styleSheet.setDeclaration("#" + id, "collapsed", collapse ? "true" : "false"); } /** * Allows you to run the demo as a standalone application. */ public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { Override public void run() { LinkBundleApp app = new LinkBundleApp(); app.init(); JFrame frame = new JFrame("Link Bundle Sample"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(550, 650); frame.getContentPane().add(app); frame.setVisible(true); } }); } private static class JComfortPanel extends JPanel { Override public void setEnabled(boolean enabled) { Component[] components = getComponents(); for (int i = 0; i < components.length; i++) components[i].setEnabled(enabled); } } }