/* * 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.ComponentOrientation; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.SystemColor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.text.MessageFormat; import java.util.Locale; import javax.swing.ImageIcon; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRootPane; import javax.swing.JTextField; import javax.swing.SwingUtilities; import ilog.views.IlvApplyObject; import ilog.views.IlvDirection; import ilog.views.IlvExpandCollapseUtil; import ilog.views.IlvGrapher; import ilog.views.IlvGraphic; import ilog.views.IlvGraphicEnumeration; import ilog.views.IlvLinkImage; import ilog.views.IlvManager; import ilog.views.IlvManagerView; import ilog.views.IlvPoint; import ilog.views.IlvRect; import ilog.views.IlvTransformer; import ilog.views.graphlayout.IlvGraphLayoutException; import ilog.views.graphlayout.IlvGraphModel; import ilog.views.graphlayout.IlvGrapherAdapter; import ilog.views.graphlayout.IlvNodeSideFilter; import ilog.views.graphlayout.hierarchical.IlvHierarchicalLayout; import ilog.views.graphlayout.link.IlvLinkLayout; import ilog.views.interactor.IlvExpandCollapseInteractor; import ilog.views.interactor.IlvZoomViewInteractor; import ilog.views.swing.IlvJManagerViewControlBar; import ilog.views.swing.IlvJScrollManagerView; import ilog.views.util.IlvImageUtil; import ilog.views.util.IlvLocaleUtil; import ilog.views.util.IlvProductUtil; import ilog.views.util.IlvResourceUtil; import ilog.views.util.swing.IlvSwingUtil; /** * This is a very simple application that shows various policies when * expanding and collapsing graphs. It shows how to use the expand utilities in * applications that are not based on CSS styling. */ public class ExpandCollapseApp extends JRootPane { static { // Test code whether the demo works in RTL locales // Locale newLocale = new Locale("he"); // IlvSwingUtil.setDefaultLocale(newLocale); } { // This sample uses JViews Diagrammer features. When deploying an // application that includes this code, you need to be in possession // of a JViews Diagrammer Deployment license. IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_Diagrammer_Deployment); } /** The grapher */ IlvGrapher grapher = new IlvGrapher(); /** The view of the grapher */ IlvManagerView mgrview = new IlvManagerView(grapher); /** An instance of the Hierarchical Layout algorithm */ IlvHierarchicalLayout hierarchicalLayout = new IlvHierarchicalLayout(); /** An instance of the Link Layout algorithm */ IlvLinkLayout linkLayout = new IlvLinkLayout(); /** A node side filter for the Link Layout algorithm */ IlvNodeSideFilter nodeSideFilter = new IlvNodeSideFilter() { Override public boolean accept(IlvGraphModel graphModel, Object link, boolean origin, Object node, int side) { if (origin) return side == IlvDirection.Right; else return side == IlvDirection.Left; } }; /** A text field to display messages */ JTextField msgLine = new JTextField(); /** The combo box for the link layout */ JComboBox<String> linkLayoutSelector = new JComboBox<String>(); /** The adjustment mode */ String actModeString = ""; int mode; /** Whether incremental layout is required */ boolean incrementalLayout = false; /** Whether we allow link layout */ boolean allowLinkLayout = true; /** Whether a separate link layout is required */ String linkLayoutSelectionString = getString("linkLayoutSelector.NoneMode"); /** A custom adjustment policy */ PolicyWithLinkLayout customPolicy = new PolicyWithLinkLayout(); /** * Initializes the application. */ public void init() { showMessage(getString("InitMessage")); // attach the grapher to the layout instances IlvGrapherAdapter adapter = new IlvGrapherAdapter(grapher); hierarchicalLayout.attach(adapter); linkLayout.attach(adapter); // change the default option for the links style hierarchicalLayout.setGlobalLinkStyle(IlvHierarchicalLayout.ORTHOGONAL_STYLE); // change the default flow direction hierarchicalLayout.setFlowDirection(IlvDirection.Right); // create the scroll manager view IlvJScrollManagerView scrollManView = new IlvJScrollManagerView(mgrview); // Some settings on the manager view and on the scroll manager view mgrview.setAntialiasing(true); mgrview.setKeepingAspectRatio(true); mgrview.setBackground(Color.white); mgrview.setForeground(SystemColor.windowText); Color xc = SystemColor.windowText; xc = new Color(255 - xc.getRed(), 255 - xc.getGreen(), 255 - xc.getBlue()); mgrview.setDefaultXORColor(xc); mgrview.setDefaultGhostColor(SystemColor.windowText); mgrview.setZoomFactorRange(0.02, 10.0); mgrview.setWheelZoomingEnabled(true); scrollManView.setWheelScrollingEnabled(true); // create the standard control bar IlvJManagerViewControlBar controlBar = new IlvJManagerViewControlBar(); controlBar.setView(mgrview); IlvExpandCollapseInteractor inter = new IlvExpandCollapseInteractor() { Override protected void expandOrCollapse(IlvGraphic obj) { if (obj instanceof IlvManager) { expandOrCollapseImpl((IlvManager) obj); } else { super.expandOrCollapse(obj); } } }; controlBar.setSelectInteractor(inter); Image selectIconImage = getImage("gif/Expand.gif"); controlBar.setSelectIcon(new ImageIcon(selectIconImage)); controlBar.setSelectToolTipText(getString("expandButton.Tooltip")); // modify the interactors such that the demo looks better ((IlvZoomViewInteractor) controlBar.getZoomViewInteractor()).setPermanent(true); // set the initial interactor mgrview.setInteractor(controlBar.getSelectInteractor()); // create the bottom panel with the layout buttons and the message line JPanel bottomPanel = new JPanel(); bottomPanel.setLayout(new BorderLayout()); bottomPanel.add(createParameterPanel(), BorderLayout.CENTER); bottomPanel.add(msgLine, BorderLayout.SOUTH); msgLine.setEditable(false); // put manager view, top panel and bottom panel together getContentPane().setLayout(new BorderLayout(0, 0)); getContentPane().add(scrollManView, BorderLayout.CENTER); getContentPane().add(controlBar, BorderLayout.NORTH); getContentPane().add(bottomPanel, BorderLayout.SOUTH); // set the component orientation according to the locale Locale loc = IlvSwingUtil.getDefaultLocale(); ComponentOrientation co = ComponentOrientation.getOrientation(loc); getContentPane().applyComponentOrientation(co); // show all mgrview.fitTransformerToContent(true); } /** * Creates the parameter panel. */ private JPanel createParameterPanel() { JPanel panel = new JPanel(); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.LINE_END; panel.setLayout(gridbag); // expansion adjustment mode parameter c.gridx = 0; c.gridy = 0; panel.add(new JLabel(getString("expandModeSelector.Label")), c); c.gridx = 1; panel.add(new JLabel(" "), c); c.gridx = 2; JComboBox<String> adjustmentModeSelector = new JComboBox<String>(); panel.add(adjustmentModeSelector, c); adjustmentModeSelector.addItem(getString("expandModeSelector.NoneMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalWithLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalWithoutLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalRecWithLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalRecWithoutLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalStableWithLinksMode")); adjustmentModeSelector .addItem(getString("expandModeSelector.OrthogonalStableWithoutLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.RadialWithLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.RadialWithoutLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.RadialRecWithLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.RadialRecWithoutLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.RadialStableWithLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.RadialStableWithoutLinksMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.IncrLayoutMode")); adjustmentModeSelector.addItem(getString("expandModeSelector.CustomMode")); adjustmentModeSelector.setSelectedIndex(5); setAdjustmentMode((String) adjustmentModeSelector.getSelectedItem()); adjustmentModeSelector.setToolTipText(getString("expandModeSelector.Tooltip")); adjustmentModeSelector.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { SuppressWarnings("unchecked") JComboBox<String> comboBox = (JComboBox<String>) event.getSource(); String modeString = (String) comboBox.getSelectedItem(); setAdjustmentMode(modeString); } }); // link layout mode parameter c.gridx = 0; c.gridy = 1; panel.add(new JLabel(getString("linkLayoutSelector.Label")), c); c.gridx = 1; panel.add(new JLabel(" "), c); c.gridx = 2; panel.add(linkLayoutSelector, c); linkLayoutSelector.addItem(getString("linkLayoutSelector.NoneMode")); linkLayoutSelector.addItem(getString("linkLayoutSelector.HierMode")); linkLayoutSelector.addItem(getString("linkLayoutSelector.GenericMode")); linkLayoutSelector.setSelectedIndex(0); linkLayoutSelector.setToolTipText(getString("linkLayoutSelector.Tooltip")); linkLayoutSelector.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { SuppressWarnings("unchecked") JComboBox<String> comboBox = (JComboBox<String>) event.getSource(); String modeString = (String) comboBox.getSelectedItem(); setLinkLayoutMode(modeString); } }); return panel; } /** * Sets the adjustment mode. */ private void setAdjustmentMode(String modeString) { if (actModeString.equals(modeString)) return; actModeString = modeString; // to test a mode, it is better to start with a clean situation, // hence load the graph new. incrementalLayout = false; if (modeString.equals(getString("expandModeSelector.NoneMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.NONE; allowLinkLayout = true; } else if (modeString.equals(getString("expandModeSelector.OrthogonalWithLinksMode"))) { // load flatter variant loadGrapher("graph2"); mode = IlvExpandCollapseUtil.ORTHOGONAL_WITH_LINKS; allowLinkLayout = false; } else if (modeString.equals(getString("expandModeSelector.OrthogonalWithoutLinksMode"))) { // load flatter variant loadGrapher("graph2"); mode = IlvExpandCollapseUtil.ORTHOGONAL_WITHOUT_LINKS; allowLinkLayout = true; } else if (modeString.equals(getString("expandModeSelector.OrthogonalRecWithLinksMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.ORTHOGONAL_RECURSIVE_WITH_LINKS; allowLinkLayout = false; } else if (modeString.equals(getString("expandModeSelector.OrthogonalRecWithoutLinksMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.ORTHOGONAL_RECURSIVE_WITHOUT_LINKS; allowLinkLayout = true; } else if (modeString.equals(getString("expandModeSelector.OrthogonalStableWithLinksMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.ORTHOGONAL_STABLE_WITH_LINKS; allowLinkLayout = false; } else if (modeString .equals(getString("expandModeSelector.OrthogonalStableWithoutLinksMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.ORTHOGONAL_STABLE_WITHOUT_LINKS; allowLinkLayout = true; } else if (modeString.equals(getString("expandModeSelector.RadialWithLinksMode"))) { // load flatter variant loadGrapher("graph2"); mode = IlvExpandCollapseUtil.RADIAL_WITH_LINKS; allowLinkLayout = false; } else if (modeString.equals(getString("expandModeSelector.RadialWithoutLinksMode"))) { // load flatter variant loadGrapher("graph2"); mode = IlvExpandCollapseUtil.RADIAL_WITHOUT_LINKS; allowLinkLayout = true; } else if (modeString.equals(getString("expandModeSelector.RadialRecWithLinksMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.RADIAL_RECURSIVE_WITH_LINKS; allowLinkLayout = false; } else if (modeString.equals(getString("expandModeSelector.RadialRecWithoutLinksMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.RADIAL_RECURSIVE_WITHOUT_LINKS; allowLinkLayout = true; } else if (modeString.equals(getString("expandModeSelector.RadialStableWithLinksMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.RADIAL_STABLE_WITH_LINKS; allowLinkLayout = false; } else if (modeString.equals(getString("expandModeSelector.RadialStableWithoutLinksMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.RADIAL_STABLE_WITHOUT_LINKS; allowLinkLayout = true; } else if (modeString.equals(getString("expandModeSelector.IncrLayoutMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.NONE; incrementalLayout = true; allowLinkLayout = false; } else if (modeString.equals(getString("expandModeSelector.CustomMode"))) { // load deeply nested variant loadGrapher("graph1"); mode = IlvExpandCollapseUtil.STABLE; incrementalLayout = false; allowLinkLayout = false; } linkLayoutSelector.setEnabled(allowLinkLayout); showMessage(getString("ExpandModeStatusMessage"), modeString); } /** * Sets the link layout mode. */ private void setLinkLayoutMode(String modeString) { linkLayoutSelectionString = modeString; showMessage(getString("LinkLayoutStatusMessage"), modeString); } /** * Expand or collapse a manager. */ private void expandOrCollapseImpl(IlvManager manager) { if (manager.getParent() == null) return; boolean collapse = !manager.isCollapsed(); // when expanding and using incremental layout, the node bounds get bigger. // The incremental layout works better if it knows about the smaller node // bounds before expanding. if (incrementalLayout && !collapse) { IlvManager node = manager; IlvManager parent = manager.getParent(); while (parent != null) { IlvHierarchicalLayout layoutOfNode = (IlvHierarchicalLayout) hierarchicalLayout.getRecursiveLayout().getLayout(parent); IlvRect bbox = layoutOfNode.getGraphModel().boundingBox(node); layoutOfNode.setIncrementalNodeBoxForExpand(node, bbox); node = parent; parent = node.getParent(); } } if (allowLinkLayout && !linkLayoutSelectionString.equals(getString("linkLayoutSelector.NoneMode"))) { // the expand/collapse behaves more stable if all links are straightened // before expand/collapse. Since there is a link layout afterwards anyway, // the links get orthogonal anyway. straightenAllLinks(); } // now expand or collapse IlvTransformer t = manager.getParent().getDrawingTransformer(mgrview); if (mode == IlvExpandCollapseUtil.STABLE) { // custom adjustment strategy IlvExpandCollapseUtil.expandOrCollapse(manager, customPolicy, mode, collapse, t, true); } else { // default adjustment strategy IlvExpandCollapseUtil.expandOrCollapse(manager, mode, collapse, t, true); } // if the mode says we want an incremental hierarchical layout, do this now if (incrementalLayout) { hierarchicalLayout.setIncrementalMode(true); hierarchicalLayout.setIncrementalAbsoluteLevelPositioning(false); hierarchicalLayout.setGlobalIncrementalNodeMovementMode(IlvHierarchicalLayout.FREE_MODE); try { hierarchicalLayout.performLayout(true, true, true); } catch (IlvGraphLayoutException ex) { ex.printStackTrace(); } } // if an additional link layout is enabled, do this now. if (allowLinkLayout) { // only if we do no incremental hierarchical layout, we might need // a link layout if (linkLayoutSelectionString.equals(getString("linkLayoutSelector.HierMode"))) { hierarchicalLayout.setIncrementalMode(true); hierarchicalLayout.setIncrementalAbsoluteLevelPositioning(true); hierarchicalLayout.setGlobalIncrementalNodeMovementMode(IlvHierarchicalLayout.FIXED_MODE); try { hierarchicalLayout.performLayout(true, true, true); } catch (IlvGraphLayoutException ex) { ex.printStackTrace(); } } else if (linkLayoutSelectionString.equals(getString("linkLayoutSelector.GenericMode"))) { linkLayout.setIncrementalMode(true); linkLayout.setNodeSideFilter(nodeSideFilter); try { linkLayout.performLayout(true, true, true); } catch (IlvGraphLayoutException ex) { ex.printStackTrace(); } } } } /** * Load a sample IVL file. * * @param fileNameBase * The base of the filename, excluding the path prefix and the * extension suffix. */ private void loadGrapher(String fileNameBase) { try { showMessage(getString("FileReadingStartMessage"), fileNameBase); grapher.deleteAll(false); try { grapher.read(IlvSwingUtil.getRelativeURL(this, "data/" + fileNameBase + ".ivl")); } catch (Exception ex1) { // This case occurs only when we pack the entire application into // one jar including the data files, and start as application grapher.read(getClass().getResource("data/" + fileNameBase + ".ivl")); } // the transformer may have been modified, so // we set the identity transformer mgrview.fitTransformerToContent(); mgrview.repaint(); showMessage(getString("FileReadingDoneMessage"), fileNameBase); } catch (Exception e) { // e.printStackTrace(); showMessage(e.getMessage()); } } /** * 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()); } void showMessage(String msgformat, Object val) { Object[] args = {val}; showMessage(MessageFormat.format(msgformat, args)); } void showMessage(String msgformat, Object val1, Object val2) { Object[] args = {val1, val2}; showMessage(MessageFormat.format(msgformat, args)); } /** * Returns a string. */ static String getString(String key) { return IlvResourceUtil.getString(key, ExpandCollapseApp.class, IlvLocaleUtil.getCurrentLocale()); } /** * Reads an image. It first reads from the jar itself, and then tries to read * fromfile. */ public Image getImage(String fileName) { Image image = null; // try to get it from jar try { image = IlvImageUtil.getImageFromFile(getClass(), fileName); } catch (IOException e) { // System.err.println("Image not found: " + fileName + " exception: " + // e.getMessage()); showMessage(getString("ImageNotFoundErrorMessage") + "<br>", fileName, e.getMessage()); } // if couldn't get it from the jar... if (image == null) image = getToolkit().getImage(fileName); // if still null... if (image == null) { // System.err.println("Could not get image " + fileName + "!"); showMessage(getString("MissingImageErrorMessage") + "<br>", fileName); } return image; } /** * Straighten all links in the grapher. */ private void straightenAllLinks() { IlvGraphicEnumeration e = grapher.getObjects(true); grapher.applyToObjects(e, new IlvApplyObject() { Override public void apply(IlvGraphic g, Object arg) { if (g instanceof IlvLinkImage) { IlvLinkImage link = (IlvLinkImage) g; link.setIntermediateLinkPoints(null, 0, 0); } } }, null, true); } /** * Allows you to run the demo as a standalone application. */ public static void main(String[] arg) { // Sun recommends that to put the entire GUI initialization into the // AWT thread SwingUtilities.invokeLater(new Runnable() { Override public void run() { ExpandCollapseApp app = new ExpandCollapseApp(); app.init(); JFrame frame = new JFrame(getString("Frame.Label")); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(600, 600); frame.getContentPane().add(app); frame.setVisible(true); } }); } // ------------------------------------------------------------------------------- /** * A custom adjustment policy that runs a hierarchical link layout inside the * adjustment (instead of running it after the expand/collapse). If a link * layout is needed, this is in general the best approach that gives the best * results, but it is also the slowest approach in stable mode. */ class PolicyWithLinkLayout implements IlvExpandCollapseUtil.CustomAdjustmentPolicy { Override public void apply(IlvManager parent, IlvManager child, IlvRect oldBounds, IlvRect newBounds, IlvTransformer t, boolean redraw) { // do an orthogonal expansion (very similar to orthWithoutLinksPolicy) // first move the child to the center of the old bounds. This is // already done by setCollapsed, most of the time, except that it is // done with respect to null transformer and we really need it with // respect to transformer t. Hence do it again. double deltaX = 0.5d * (oldBounds.width - newBounds.width); double deltaY = 0.5d * (oldBounds.height - newBounds.height); IlvPoint p = new IlvPoint(oldBounds.x + deltaX, oldBounds.y + deltaY); if (t != null) t.inverse(p); parent.moveObject(child, p.x, p.y, redraw); // shift all nodes in x or y direction double minX = oldBounds.x; double maxX = oldBounds.x + oldBounds.width; double centerX = oldBounds.x + 0.5 * oldBounds.width; double minY = oldBounds.y; double maxY = oldBounds.y + oldBounds.height; double centerY = oldBounds.y + 0.5 * oldBounds.height; IlvGraphicEnumeration e = parent.getObjects(); while (e.hasMoreElements()) { IlvGraphic g = e.nextElement(); if (g == child) continue; if (g instanceof IlvLinkImage) continue; IlvRect bbox = g.boundingBox(t); p.setLocation(bbox.x, bbox.y); double cx = bbox.x + 0.5 * bbox.width; double cy = bbox.y + 0.5 * bbox.height; if (cx > maxX) p.x -= deltaX; else if (cx < minX) p.x += deltaX; else p.x -= (deltaX * 2 * (cx - centerX) / oldBounds.width); if (cy > maxY) p.y -= deltaY; else if (cy < minY) p.y += deltaY; else p.y -= (deltaY * 2 * (cy - centerY) / oldBounds.height); if (t != null) t.inverse(p); parent.moveObject(g, p.x, p.y, redraw); } // do a link layout (using the hierarchical layout) on the parent IlvHierarchicalLayout layout = new IlvHierarchicalLayout(); layout.attach((IlvGrapher) parent); layout.setGlobalLinkStyle(IlvHierarchicalLayout.ORTHOGONAL_STYLE); layout.setFlowDirection(IlvDirection.Right); layout.setIncrementalMode(true); layout.setIncrementalAbsoluteLevelPositioning(true); layout.setGlobalIncrementalNodeMovementMode(IlvHierarchicalLayout.FIXED_MODE); try { // perform layout only on parent, not recursively on children layout.performLayout(true, true); } catch (IlvGraphLayoutException ex) { ex.printStackTrace(); } layout.detach(); } } }