/* * 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.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.geom.RoundRectangle2D; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRootPane; import javax.swing.JToggleButton; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import ilog.views.IlvDefaultManagerFrame; import ilog.views.IlvGrapher; import ilog.views.IlvGraphic; import ilog.views.IlvHandlesSelection; import ilog.views.IlvLinkImage; import ilog.views.IlvManagerView; import ilog.views.IlvManagerViewInteractor; import ilog.views.IlvPoint; import ilog.views.IlvRect; import ilog.views.accelerator.IlvDeleteSelectionAccelerator; import ilog.views.accelerator.IlvFitToSizeAccelerator; import ilog.views.accelerator.IlvIdentityAccelerator; import ilog.views.accelerator.IlvRotateAccelerator; import ilog.views.accelerator.IlvScrollDownAccelerator; import ilog.views.accelerator.IlvScrollLeftAccelerator; import ilog.views.accelerator.IlvScrollRightAccelerator; import ilog.views.accelerator.IlvScrollUpAccelerator; import ilog.views.accelerator.IlvSelectAllAccelerator; import ilog.views.accelerator.IlvZoomInAccelerator; import ilog.views.accelerator.IlvZoomOutAccelerator; import ilog.views.event.InteractorChangedEvent; import ilog.views.event.InteractorListener; import ilog.views.graphic.IlvGeneralPath; import ilog.views.graphic.IlvPolylineLinkImage; import ilog.views.graphic.IlvRectangle; import ilog.views.graphic.IlvText; import ilog.views.graphic.composite.IlvCompositeGraphic; import ilog.views.graphic.composite.IlvEventMap; import ilog.views.graphic.composite.layout.IlvAttachmentConstraint; import ilog.views.graphic.composite.layout.IlvAttachmentLayout; import ilog.views.graphic.composite.layout.IlvAttachmentLocation; import ilog.views.graphic.composite.layout.IlvStackerLayout; import ilog.views.graphic.composite.objectinteractor.IlvCompositeContext; import ilog.views.graphic.composite.objectinteractor.IlvCompositeInteractor; import ilog.views.interactor.IlvMakePolyLinkInteractor; import ilog.views.interactor.IlvSelectInteractor; import ilog.views.swing.IlvJScrollManagerView; import ilog.views.util.swing.IlvDecimalNumberField; import ilog.views.util.swing.IlvJComboBox; import ilog.views.util.swing.IlvSwingUtil; /** * This sample shows how to use the utility method * <code>ilog.views.IlvGrapher#setVisibleBranch(IlvGraphic, int, boolean, boolean)</code>. */ SuppressWarnings("serial") public class ShowHideBranch extends JRootPane { { // This sample uses JViews Diagrammer features. When deploying an // application that includes this code, you need to be in possession // of a Perforce JViews Diagrammer Deployment license. // IlvProductUtil.DeploymentLicenseRequired( // IlvProductUtil.JViews_Diagrammer_Deployment); } IlvGrapher grapher; IlvManagerView mgrview; /** The panel containing buttons. */ JPanel options; /** Array of buttons. */ InteractorButton[] interactorButtons; private double minpos = 0; private int startLevel = 0; private int endLevel = -1; boolean intergraphLinks = false; boolean treeExtIntergraphLinks = false; /** The tree is created with this number of children for each node. */ private static final int NUMBER_OF_CHILDREN = 2; /** How large the nodes should be. */ private static final double NODE_WIDTH = 45; private static final double NODE_HEIGHT = 35; /** For laying out the nodes **/ private static final int MIN_XSPACE = 60; private static final int MIN_YSPACE = 20; /** How thick the links should be. */ private static final double LINK_WIDTH = 2; /** * Initializes the app. */ public void init() { getContentPane().setLayout(new BorderLayout(0, 0)); // Creates the grapher. grapher = new IlvGrapher(); // Creates the view of the grapher. mgrview = new IlvManagerView(grapher, null); mgrview.setKeepingAspectRatio(true); mgrview.setAntialiasing(true); mgrview.addInteractorListener(new InteractorListener() { Override public void interactorChanged(InteractorChangedEvent evt) { IlvManagerViewInteractor newI = evt.getNewValue(); for (int i = 0; i < interactorButtons.length; i++) { boolean select = interactorButtons[i].getInteractor() == newI; if (select != interactorButtons[i].isSelected()) { interactorButtons[i].removeItemListener(interactorButtons[i]); interactorButtons[i].setSelected(select); interactorButtons[i].addItemListener(interactorButtons[i]); } } } }); // Setting parameters for selection handles. IlvHandlesSelection.defaultHandleColor = Color.black; IlvHandlesSelection.defaultHandleBackgroundColor = Color.white; IlvHandlesSelection.defaultHandleShape = IlvHandlesSelection.SQUARE_SHAPE; // Adds the graphic objects to the grapher. minpos = 0; addSubtree(0, 4, grapher); // Adds some accelerators. grapher.addAccelerator(new IlvIdentityAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_I, 0)); grapher.addAccelerator( new IlvZoomOutAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_U, KeyEvent.CTRL_MASK)); grapher.addAccelerator( new IlvZoomInAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_Z, KeyEvent.CTRL_MASK)); grapher.addAccelerator(new IlvFitToSizeAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_F, 0)); grapher.addAccelerator(new IlvRotateAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_R, 0)); grapher.addAccelerator( new IlvSelectAllAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_A, KeyEvent.CTRL_MASK)); grapher.addAccelerator(new IlvScrollUpAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_UP, 0)); grapher.addAccelerator(new IlvScrollDownAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_DOWN, 0)); grapher .addAccelerator(new IlvScrollRightAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_RIGHT, 0)); grapher.addAccelerator(new IlvScrollLeftAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_LEFT, 0)); IlvJScrollManagerView scview = new IlvJScrollManagerView(mgrview); mgrview.setWheelZoomingEnabled(true); scview.setWheelScrollingEnabled(true); getContentPane().add(scview, BorderLayout.CENTER); // Allows the user to delete nodes by pressing the "del" key. grapher.addAccelerator( new IlvDeleteSelectionAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_DELETE, 0)); // Creates the options panel. JPanel optionsPanel; getContentPane().add(optionsPanel = new JPanel(), BorderLayout.WEST); optionsPanel.setLayout(new BorderLayout(0, 0)); options = new JPanel(); optionsPanel.add(options, BorderLayout.NORTH); options.setLayout(new GridLayout(0, 1, 1, 1)); initOptionsPanel(); } /** * Adds one interactor button. */ void addButtonInteractor(int index, String text, String tooltip, IlvManagerViewInteractor inter) { interactorButtons[index] = new InteractorButton(text, inter, tooltip, this); options.add(interactorButtons[index]); interactorButtons[index].setSize(20, 20); } /** * Adds all the interactor buttons and options GUI. */ void initOptionsPanel() { boolean opaque = true; // set the options in opaque mode int n = 0; interactorButtons = new InteractorButton[1]; IlvSelectInteractor selInter = new IlvSelectInteractor(); selInter.setOpaqueDragSelection(opaque); selInter.setOpaqueMove(opaque); selInter.setOpaqueResize(opaque); selInter.setOpaquePolyPointsEdition(opaque); selInter.setObjectInteractorEnabled(true); IlvMakePolyLinkInteractor makePolyLink = new IlvMakePolyLinkInteractor() { Override public IlvLinkImage createObject(IlvGraphic from, IlvGraphic to, IlvPoint[] points, boolean oriented) { IlvLinkImage link = super.createObject(from, to, points, oriented); link.setLineWidth(LINK_WIDTH); link.setOriented(true); return link; } }; makePolyLink.setOpaqueMode(opaque); makePolyLink.setPermanent(false); makePolyLink.setForeground(Color.blue); addButtonInteractor(n++, "Create Link", "Allows the creation of a polyline links", makePolyLink); // ============== // level final IlvDecimalNumberField startLevelField = new IlvDecimalNumberField(0, 1000, false); startLevelField.setText("0"); startLevelField.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { String text = startLevelField.getText(); Integer levelInt = Integer.valueOf(text); startLevel = levelInt.intValue(); } }); options.add(new JLabel("Start Level:")); options.add(startLevelField); final IlvDecimalNumberField endLevelField = new IlvDecimalNumberField(-1, 1000, false); endLevelField.setText("-1"); endLevelField.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { String text = endLevelField.getText(); Integer levelInt = Integer.valueOf(text); endLevel = levelInt.intValue(); } }); options.add(new JLabel("End Level:")); options.add(endLevelField); // ============ // Intergraph links SuppressWarnings("unchecked") final JComboBox<String> linksModeBox = new IlvJComboBox(); linksModeBox.addItem("Normal links"); linksModeBox.addItem("Explicit intergraph links"); linksModeBox.addItem("Implicit intergraph links"); linksModeBox.addItem("Implicit subgraphs"); linksModeBox.setEnabled(false); linksModeBox.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { SuppressWarnings("unchecked") JComboBox<String> comboBox = (JComboBox<String>) event.getSource(); String sel = (String) comboBox.getSelectedItem(); if (sel.equals("Normal links")) { intergraphLinks = false; treeExtIntergraphLinks = false; } else if (sel.equals("Explicit intergraph links")) { intergraphLinks = true; treeExtIntergraphLinks = false; } else if (sel.equals("Implicit intergraph links")) { intergraphLinks = true; treeExtIntergraphLinks = true; } else if (sel.equals("Implicit subgraphs")) { intergraphLinks = false; treeExtIntergraphLinks = true; } } }); // ============ // nested final JCheckBox nestedCheckBox = new JCheckBox("Nested Graph", false); nestedCheckBox.addItemListener(new ItemListener() { Override public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { minpos = 0; grapher.deleteAll(false); addNestedSubtree(0, 2, grapher); grapher.reDraw(); linksModeBox.setEnabled(true); } else { minpos = 0; grapher.deleteAll(false); addSubtree(0, 4, grapher); grapher.reDraw(); intergraphLinks = false; treeExtIntergraphLinks = false; linksModeBox.setSelectedIndex(0); linksModeBox.setEnabled(false); } } }); options.add(nestedCheckBox); options.add(linksModeBox); } /** * Adds an object to the grapher. * It also adds the connection pin to the node. */ void addNode(IlvGraphic node) { grapher.addNode(node, 1, false); } public void start() { // Ensures that the root object is in the middle. mgrview.translate(10, -grapher.boundingBox().height / 2 + getHeight() / 2, true); } /** * Generates a subtree and layout it. If ilog.views.graphlayout package * is available, this can advantageously be replaced by using a * <code>ilog.views.graphlayout.tree.IlvTreeLayout</code>. * @param actlevel the actual level * @param maxlevel the maximal level * @param grapher the grapher * @return The root of the subtree. */ private IlvGraphic addSubtree(int actlevel, int maxlevel, IlvGrapher grapher) { Color color = new Color(Math.min(50 + actlevel * 40, 255), Math.min(50 + actlevel * 40, 255), Math.min(100 + actlevel * 40, 255)); IlvGraphic node = createNode(color); addNode(node); IlvRect rect; IlvGraphic child; IlvLinkImage link; double minChildPos = Double.MAX_VALUE; double maxChildPos = -Double.MAX_VALUE; if (actlevel < maxlevel) { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { child = addSubtree(actlevel + 1, maxlevel, grapher); rect = child.boundingBox(); minpos = Math.max(minpos, rect.y + rect.height + MIN_YSPACE); minChildPos = Math.min(minChildPos, rect.y); maxChildPos = Math.max(maxChildPos, rect.y + rect.height); link = new IlvPolylineLinkImage(node, child, true, null); link.setForeground(color); link.setLineWidth(2); grapher.addLink(link, 0, false); } } if (minChildPos < maxChildPos) grapher.moveObject(node, actlevel * (NODE_WIDTH + MIN_XSPACE), 0.5d * (maxChildPos + minChildPos - NODE_HEIGHT), false); else grapher.moveObject(node, actlevel * (NODE_WIDTH + MIN_XSPACE), minpos, false); return node; } private IlvGraphic addNestedSubtree(int actlevel, int maxlevel, IlvGrapher grapher) { Color color = new Color(Math.min(50 + actlevel * 40, 255), Math.min(50 + actlevel * 40, 255), Math.min(100 + actlevel * 40, 255)); IlvLinkImage link; IlvGrapher subgraph = new IlvGrapher(); IlvGraphic node1 = createNode(color); subgraph.addNode(node1, false); subgraph.moveObject(node1, 0, -0.5 * NODE_HEIGHT, false); IlvGraphic node2 = createNode(color); subgraph.addNode(node2, false); subgraph.moveObject(node2, NODE_WIDTH + MIN_XSPACE, -0.5 * NODE_HEIGHT, false); link = new IlvPolylineLinkImage(node1, node2, true, null); link.setForeground(color); link.setLineWidth(2); subgraph.addLink(link, 0, false); IlvGraphic node3 = null; if (actlevel < maxlevel) { node3 = createNode(color); subgraph.addNode(node3, false); link = new IlvPolylineLinkImage(node1, node3, true, null); link.setForeground(color); link.setLineWidth(2); subgraph.addLink(link, 0, false); } // add an invisible dummy object so that the subgraph stays at its // position even when all real nodes get invisible IlvGraphic dummy = new IlvRectangle( new IlvRect(0, -0.5 * NODE_HEIGHT, 2 * NODE_WIDTH + MIN_XSPACE, NODE_HEIGHT)); dummy.setForeground(new Color(0, 0, 0, 0)); dummy.setBackground(new Color(0, 0, 0, 0)); subgraph.addObject(dummy, 0, false); // increase the margins of the manager frame IlvDefaultManagerFrame frame = (IlvDefaultManagerFrame) subgraph.getFrame(); frame.setLeftMargin(10); frame.setRightMargin(10); frame.setOpaque(true); frame.setBackground(Color.yellow); addNode(subgraph); IlvRect rect; IlvGraphic child; IlvGraphic childBag; double minChildPos = Double.MAX_VALUE; double maxChildPos = -Double.MAX_VALUE; double childHeight = 0; if (actlevel < maxlevel) { for (int i = 0; i < 4; i++) { child = addNestedSubtree(actlevel + 1, maxlevel, grapher); childBag = (IlvGraphic) child.getGraphicBag(); rect = childBag.boundingBox(); childHeight = rect.height; minpos = Math.max(minpos, rect.y + rect.height + MIN_YSPACE); minChildPos = Math.min(minChildPos, rect.y); maxChildPos = Math.max(maxChildPos, rect.y + rect.height); switch (i) { case 0: link = new IlvPolylineLinkImage(node2, child, true, null); break; case 1: link = new IlvPolylineLinkImage(subgraph, child, true, null); break; case 2: link = new IlvPolylineLinkImage(subgraph, childBag, true, null); break; case 3: link = new IlvPolylineLinkImage(node3, childBag, true, null); break; } link.setForeground(color); link.setLineWidth(2); grapher.addLink(link, 2, false); } } if (minChildPos < maxChildPos) { double dist = (maxChildPos - minChildPos - childHeight) / 2; subgraph.moveObject(node2, NODE_WIDTH + MIN_XSPACE, -dist - 0.5 * NODE_HEIGHT, false); subgraph.moveObject(node3, NODE_WIDTH + MIN_XSPACE, dist - 0.5 * NODE_HEIGHT, false); subgraph.reshapeObject(dummy, new IlvRect(0, -dist - 0.5 * NODE_HEIGHT, 2 * NODE_WIDTH + MIN_XSPACE, 2 * dist + NODE_HEIGHT), false); double height = subgraph.boundingBox().height; grapher.moveObject(subgraph, 2 * actlevel * (NODE_WIDTH + MIN_XSPACE), 0.5 * (maxChildPos + minChildPos - height), false); } else { grapher.moveObject(subgraph, 2 * actlevel * (NODE_WIDTH + MIN_XSPACE), minpos, false); } return node1; } private IlvGraphic createNode(Color color) { final IlvCompositeGraphic node = new IlvCompositeGraphic(); // Sets the layout IlvAttachmentLayout layout = new IlvAttachmentLayout(); node.setLayout(layout); // Creates a white rectangle RoundRectangle2D.Double shape = new RoundRectangle2D.Double(0, 0, NODE_WIDTH, NODE_HEIGHT, 10, 10); IlvGeneralPath path = new IlvGeneralPath(shape); path.setFillOn(true); path.setStroke(new BasicStroke(2)); path.setForeground(Color.black); path.setBackground(Color.white); node.setChildren(0, path); // Creates the container for stacking the icons horizontally to the right IlvCompositeGraphic iconsTop = new IlvCompositeGraphic(); IlvStackerLayout stackerTop = new IlvStackerLayout(SwingConstants.RIGHT, SwingConstants.CENTER, 2); iconsTop.setLayout(stackerTop); IlvCompositeGraphic iconsBottom = new IlvCompositeGraphic(); IlvStackerLayout stackerBottom = new IlvStackerLayout(SwingConstants.RIGHT, SwingConstants.CENTER, 2); iconsBottom.setLayout(stackerBottom); // Create the decoration buttons IlvGraphic showOrig = createShowHideButtonDeco(true, true); iconsTop.setChildren(2, showOrig); IlvGraphic hideOrig = createShowHideButtonDeco(true, false); iconsBottom.setChildren(2, hideOrig); IlvGraphic showDest = createShowHideButtonDeco(false, true); iconsTop.setChildren(1, showDest); IlvGraphic hideDest = createShowHideButtonDeco(false, false); iconsBottom.setChildren(1, hideDest); // Attach the TopCenter (hotspot) of the stacker to the // TopCenter (anchor) of the rectangle node.setChildren(1, iconsTop); node.setConstraints(1, new IlvAttachmentConstraint(IlvAttachmentLocation.TopCenter, IlvAttachmentLocation.TopCenter, 0, +1)); // Attach the BottomCenter (hotspot) of the stacker to the // BottomCenter (anchor) of the rectangle node.setChildren(2, iconsBottom); node.setConstraints(2, new IlvAttachmentConstraint(IlvAttachmentLocation.BottomCenter, IlvAttachmentLocation.BottomCenter, 0, -1)); Action[] actionsShowOrig = {new AbstractAction() { Override public void actionPerformed(ActionEvent e) { grapher.setVisibleBranch(node, startLevel, endLevel, true, true, intergraphLinks, treeExtIntergraphLinks); grapher.reDraw(); } },}; Action[] actionsHideOrig = {new AbstractAction() { Override public void actionPerformed(ActionEvent e) { grapher.setVisibleBranch(node, startLevel, endLevel, false, true, intergraphLinks, treeExtIntergraphLinks); grapher.reDraw(); } },}; Action[] actionsShowDest = {new AbstractAction() { Override public void actionPerformed(ActionEvent e) { grapher.setVisibleBranch(node, startLevel, endLevel, true, false, intergraphLinks, treeExtIntergraphLinks); grapher.reDraw(); } },}; Action[] actionsHideDest = {new AbstractAction() { Override public void actionPerformed(ActionEvent e) { grapher.setVisibleBranch(node, startLevel, endLevel, false, false, intergraphLinks, treeExtIntergraphLinks); grapher.reDraw(); } },}; String[] events = {"BUTTON1_CLICKED",}; // Show Origin IlvEventMap eventMapShowOrig = new IlvEventMap(); eventMapShowOrig.setActions(actionsShowOrig); eventMapShowOrig.setEvents(events); iconsTop.setEventMaps(2, eventMapShowOrig); // Hide Origin IlvEventMap eventMapHideOrig = new IlvEventMap(); eventMapHideOrig.setActions(actionsHideOrig); eventMapHideOrig.setEvents(events); iconsBottom.setEventMaps(2, eventMapHideOrig); // Show Destination IlvEventMap eventMapShowDest = new IlvEventMap(); eventMapShowDest.setActions(actionsShowDest); eventMapShowDest.setEvents(events); iconsTop.setEventMaps(1, eventMapShowDest); // Hide Destination IlvEventMap eventMapHideDest = new IlvEventMap(); eventMapHideDest.setActions(actionsHideDest); eventMapHideDest.setEvents(events); iconsBottom.setEventMaps(1, eventMapHideDest); IlvCompositeInteractor compInteractor = new IlvCompositeInteractor() { Override public IlvCompositeContext getCompositeContext(Object source) { return null; } }; node.setObjectInteractor(compInteractor); return node; } /** * Creates the decoration of the composite object (the node of * the grapher) for the button which allows either to expand or to * collapse a branch in a given direction. */ private IlvGraphic createShowHideButtonDeco(boolean origin, boolean show) { // "o" stands for "origin", "d" for destination // "+" stands for "expand", "-" for collapse String text = (origin ? "o" : "d") + (show ? "+" : "-"); IlvText buttonDeco = new IlvText(new IlvPoint(), text); buttonDeco.setFillOn(true); Color fillColor = show ? Color.green : Color.red; fillColor = fillColor.brighter().brighter().brighter(); buttonDeco.setFillPaint(fillColor); double width = buttonDeco.boundingBox().width; buttonDeco.setLeftMargin(Math.max(0, (20 - width) / 2)); buttonDeco.setRightMargin(Math.max(0, (20 - width) / 2)); return buttonDeco; } /** * A button class that automatically installs an interactor. */ private class InteractorButton extends JToggleButton implements ItemListener { private IlvManagerViewInteractor interactor; private ShowHideBranch editor; public InteractorButton(String text, IlvManagerViewInteractor inter, String tooltip, ShowHideBranch editor) { super(text); this.editor = editor; this.interactor = inter; setToolTipText(tooltip); addItemListener(this); } IlvManagerViewInteractor getInteractor() { return interactor; } Override public void itemStateChanged(ItemEvent event) { if (isSelected()) { // Removes the previous interactor and installs a new one. editor.mgrview.popInteractor(); editor.mgrview.pushInteractor(interactor); } else editor.mgrview.popInteractor(); } } public static void main(String[] args) { // Sun recommends that to put the entire GUI initialization into the // AWT thread SwingUtilities.invokeLater(new Runnable() { Override public void run() { ShowHideBranch app = new ShowHideBranch(); app.init(); JFrame frame = new JFrame("Show-Hide Grapher Branch"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(700, 600); frame.getContentPane().add(app); frame.setVisible(true); } }); } }