/* * 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.ComponentOrientation; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.Image; import java.awt.Insets; 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.awt.event.KeyEvent; import java.text.MessageFormat; import java.util.Locale; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; 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.JToggleButton; import javax.swing.SwingUtilities; import ilog.views.IlvAccelerator; import ilog.views.IlvApplyObject; import ilog.views.IlvGrapher; import ilog.views.IlvGraphic; import ilog.views.IlvGraphicBag; import ilog.views.IlvGraphicEnumeration; import ilog.views.IlvGraphicVector; import ilog.views.IlvHandlesSelection; import ilog.views.IlvLinkConnector; import ilog.views.IlvLinkImage; import ilog.views.IlvManager; import ilog.views.IlvManagerView; import ilog.views.IlvObjectWithSelection; import ilog.views.IlvPoint; import ilog.views.IlvRect; import ilog.views.IlvStroke; import ilog.views.graphic.IlvIcon; import ilog.views.graphic.IlvPolylineLinkImage; import ilog.views.graphic.IlvRectangle; import ilog.views.graphic.IlvReliefRectangle; import ilog.views.graphic.IlvSplineLinkImage; import ilog.views.graphic.IlvZoomableLabel; import ilog.views.graphic.composite.IlvCompositeGraphic; import ilog.views.graphic.composite.IlvCompositeLink; import ilog.views.graphic.composite.layout.IlvAttachmentConstraint; import ilog.views.graphic.composite.layout.IlvAttachmentLocation; import ilog.views.graphic.composite.layout.IlvCenteredLayout; import ilog.views.graphic.composite.layout.IlvLinkAttachmentLocation; import ilog.views.graphic.composite.layout.IlvStackerLayout; import ilog.views.graphic.linkbundle.IlvDefaultLinkBundleFrame; import ilog.views.graphic.linkbundle.IlvLinkBundle; import ilog.views.graphic.linkbundle.IlvLinkBundleFrame; import ilog.views.graphlayout.IlvGraphLayout; import ilog.views.graphlayout.IlvGraphLayoutException; import ilog.views.graphlayout.IlvGraphModel; import ilog.views.graphlayout.IlvGrapherAdapter; import ilog.views.graphlayout.IlvInappropriateLinkException; import ilog.views.graphlayout.link.IlvLinkLayout; import ilog.views.interactor.IlvSelectInteractor; import ilog.views.interactor.IlvZoomViewInteractor; import ilog.views.linkconnector.IlvClippingLinkConnector; import ilog.views.linkconnector.IlvFreeLinkConnector; import ilog.views.swing.IlvJManagerViewControlBar; import ilog.views.swing.IlvJScrollManagerView; import ilog.views.swing.IlvPopupMenuContext; import ilog.views.swing.IlvPopupMenuManager; import ilog.views.swing.IlvSimplePopupMenu; import ilog.views.swing.IlvToolTipManager; 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.IlvJSpinnerDecimalNumberField; import ilog.views.util.swing.IlvSwingUtil; /** * The main class LinkBundleApp. This demonstrates the class * <code>ilog.views.graphic.linkbundle.IlvLinkBundle</code>. */ public class LinkBundleApp 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, null); /** The control bar */ IlvJManagerViewControlBar controlBar = new IlvJManagerViewControlBar(); /** An instance of the Link Layout algorithm */ IlvLinkLayout linkLayout = new IlvLinkLayout(); private int linkStyle = ORTHOGONAL_LINK_STYLE; /** counter just to have newly create links in nice colors */ int colorCounter = 0; private JCheckBox singleConnectionPointCheckbox = new JCheckBox(getString("singleConnectionPointCheckbox.Label"), false); private boolean singleConnectionPoint = false; private boolean linkBundleFrameEnabled = true; private boolean withNestedBundles = false; private double offset = 5; private double frameMargin = 3; private boolean expandedState = true; private boolean subbundleFlag = true; private static final double NODE_WIDTH = 100; private static final double NODE_HEIGHT = 100; private static final double SUBLINKS_WIDTH = 5; private static final double OVERVIEW_LINK_WIDTH = 15; private static final int N_SUBLINKS_IN_TOP_BUNDLE = 3; private static final int N_SUBLINKS_IN_SUB_BUNDLE = 2; private static int NODE_LAYER = 0; private static int LINK_LAYER = 1; // link style options private static final int ORTHOGONAL_LINK_STYLE = 0; private static final int DIRECT_LINK_STYLE = 1; private static final int STRAIGHT_LINK_STYLE = 2; // link connector modes private static final int FREE_LINK_CONNECTOR_ON_NODES = 0; private static final int CLIPPING_LINK_CONNECTOR_ON_NODES = 1; private static final int CLIPPING_LINK_CONNECTOR_ON_NODES_AND_FREE_ON_SUBLINKS = 2; // Colors private static final Color NODE_COLOR = new Color(154, 104, 3); private static final Color OVERVIEW_LINK_COLOR = new Color(3, 47, 189); private static final Color SUBLINK_COLOR = OVERVIEW_LINK_COLOR; private static final Color TOP_BUNDLE_FRAME_COLOR = Color.yellow; private static final Color SUB_BUNDLE_FRAME_COLOR1 = new Color(186, 186, 186); private static final Color SUB_BUNDLE_FRAME_COLOR2 = new Color(198, 233, 244); private static final boolean TOOLTIPS_ENABLED = true; private static final boolean WITH_CONTROL_BAR = true; /** * Initializes the application. */ public void init() { // Not needed here: enable the expand/collapse icons // mgrview.setCollapseExpandIconsEnabled(true); // attach the grapher to the layout instances IlvGrapherAdapter adapter = new IlvGrapherAdapter(grapher); linkLayout.setAutoLayout(true); linkLayout.setLinkOffset(4); linkLayout.setMinFinalSegmentLength(10); linkLayout.attach(adapter); // for the pop-up menu // register view view to the pop-up manager IlvPopupMenuManager.registerView(mgrview); // register the menus under different names IlvPopupMenuManager.registerMenu("COLLAPSE_BUNDLE", createCollapseMenu()); IlvPopupMenuManager.registerMenu("EXPAND_BUNDLE", createExpandMenu()); // 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.setWheelZoomingEnabled(true); scrollManView.setWheelScrollingEnabled(true); if (TOOLTIPS_ENABLED) IlvToolTipManager.registerView(mgrview); // Settings parameters for selection handles IlvHandlesSelection.defaultHandleColor = Color.black; IlvHandlesSelection.defaultHandleBackgroundColor = Color.white; IlvHandlesSelection.defaultHandleShape = IlvHandlesSelection.SQUARE_SHAPE; // set the layout manager JPanel topPanel = new JPanel(); JPanel bottomPanel = new JPanel(); JPanel bottomPanel1 = new JPanel(); JPanel bottomPanel2 = new JPanel(); topPanel.setLayout(new FlowLayout(FlowLayout.LEADING)); bottomPanel.setLayout(new GridLayout(2, 0)); bottomPanel1.setLayout(new FlowLayout(FlowLayout.LEADING)); bottomPanel2.setLayout(new FlowLayout(FlowLayout.LEADING)); bottomPanel.add(bottomPanel1); bottomPanel.add(bottomPanel2); // fit so far all together getContentPane().setLayout(new BorderLayout(0, 0)); getContentPane().add("Center", scrollManView); getContentPane().add("North", topPanel); getContentPane().add("South", bottomPanel); // create the standard control bar if (WITH_CONTROL_BAR) { controlBar.setView(mgrview); // modify the interactors such that the demo looks better IlvSelectInteractor selInteractor = (IlvSelectInteractor) controlBar.getSelectInteractor(); selInteractor.setOpaqueMove(true); selInteractor.setOpaqueResize(true); selInteractor.setOpaquePolyPointsEdition(true); ((IlvZoomViewInteractor) controlBar.getZoomViewInteractor()).setPermanent(true); topPanel.add(controlBar); } // accelerator to allow deletion of bends and entire nodes and links grapher.addAccelerator(new IlvAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_DELETE, 0) { Override protected boolean handleEvent(IlvManagerView v) { grapher.deleteSelections(true, true); return true; } }); // -------------------------------------------------------- // Add the sample graph createSampleGraph(true, true, false, false, isLinkBundleFrameEnabled()); // ------------------------------------------------------- // set the initial interactor if (WITH_CONTROL_BAR) mgrview.setInteractor(controlBar.getSelectInteractor()); // create the layout buttons JButton b; String tooltip; // ---------------------------------------- // Link style bottomPanel1.add(new JLabel(getString("linkStyleChooser.Label"))); JComboBox<String> linkStyleChooser = new JComboBox<String>(); bottomPanel1.add(linkStyleChooser); linkStyleChooser.setToolTipText(getString("linkStyleChooser.Tooltip")); linkStyleChooser.addItem(getString("linkStyleChooser.OrthMode")); linkStyleChooser.addItem(getString("linkStyleChooser.DirectMode")); linkStyleChooser.addItem(getString("linkStyleChooser.StraightMode")); linkStyleChooser.addItemListener(new ItemListener() { Override public void itemStateChanged(ItemEvent event) { if (event.getStateChange() == ItemEvent.DESELECTED) return; String styleName = (String) event.getItem(); if (styleName.equals(getString("linkStyleChooser.OrthMode"))) setLinkStyle(ORTHOGONAL_LINK_STYLE); else if (styleName.equals(getString("linkStyleChooser.DirectMode"))) setLinkStyle(DIRECT_LINK_STYLE); else if (styleName.equals(getString("linkStyleChooser.StraightMode"))) setLinkStyle(STRAIGHT_LINK_STYLE); else throw new RuntimeException("Unsupported link style: " + styleName); } }); setLinkStyle(getLinkStyle()); // ---------------------------------------- // Single connection point bottomPanel1.add(singleConnectionPointCheckbox); singleConnectionPointCheckbox.setToolTipText(getString("singleConnectionPointCheckbox.Tooltip")); singleConnectionPointCheckbox.addItemListener(new ItemListener() { Override public void itemStateChanged(ItemEvent e) { boolean single = false; boolean selectionChanged = false; if (e.getStateChange() == ItemEvent.SELECTED) { selectionChanged = true; single = true; } else if (e.getStateChange() == ItemEvent.DESELECTED) { selectionChanged = true; single = false; } if (selectionChanged) setSingleConnectionPoint(single, true); } }); // -------------------------------------------- // Offset JLabel offsetLabel = new JLabel(getString("Offset.Label")); tooltip = getString("Offset.Tooltip"); offsetLabel.setToolTipText(tooltip); bottomPanel1.add(offsetLabel); final IlvJSpinnerDecimalNumberField offsetField = new IlvJSpinnerDecimalNumberField(0, 99, getOffset(), true, 4, 1); offsetField.setToolTipText(tooltip); bottomPanel1.add(offsetField); offsetField.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { final double value = offsetField.getDoubleValue(); setOffset(value); applyToAllLinkBundles(grapher, new IlvApplyObject() { Override public void apply(IlvGraphic obj, Object arg) { ((IlvLinkBundle) obj).setOffset(value); } }); } }); // ----------------------------------------------- // Link Bundle Frame final JPanel bundleFramePanel = new JComfortPanel(); final JLabel marginLabel = new JLabel(getString("frameMargin.Label")); tooltip = getString("frameMargin.Tooltip"); marginLabel.setToolTipText(tooltip); final IlvJSpinnerDecimalNumberField marginField = new IlvJSpinnerDecimalNumberField(0, 99, getFrameMargin(), true, 4, 1); marginField.setToolTipText(tooltip); marginField.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { final double value = marginField.getDoubleValue(); setFrameMargin(value); applyToAllLinkBundles(grapher, new IlvApplyObject() { Override public void apply(IlvGraphic obj, Object arg) { IlvLinkBundle linkBundle = (IlvLinkBundle) obj; IlvLinkBundleFrame frame = linkBundle.getFrame(); if (frame instanceof IlvDefaultLinkBundleFrame) ((IlvDefaultLinkBundleFrame) frame).setMargin(value); } }); } }); final JCheckBox bundleFrameCheckbox = new JCheckBox(getString("bundleFrameCheckbox.Label"), true); bundleFrameCheckbox.setToolTipText(getString("bundleFrameCheckbox.Tooltip")); bundleFrameCheckbox.addItemListener(new ItemListener() { Override public void itemStateChanged(ItemEvent e) { // specify the new link style option if (e.getStateChange() == ItemEvent.SELECTED) { setLinkBundleFrameEnabled(true); marginLabel.setEnabled(true); marginField.setEnabled(true); } else if (e.getStateChange() == ItemEvent.DESELECTED) { setLinkBundleFrameEnabled(false); marginLabel.setEnabled(false); marginField.setEnabled(false); } // reapply the layout layout(linkLayout); } }); bundleFramePanel.add(bundleFrameCheckbox); bundleFramePanel.add(marginLabel); bundleFramePanel.add(marginField); bottomPanel2.add(bundleFramePanel); // ---------------------------------------- // Expand/collapse button bottomPanel2.add(b = new JButton(getString("collapseButton.Label"))); refreshExpandCollapseButton(b, expandedState); final JButton expandCollapseButton = b; b.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); } setCollapsed(!expandedState); grapher.reDraw(); } }); // ------------------------------------------- // Top panel topPanel.add(new JLabel(getString("fileChooser.Label"))); JComboBox<String> chooser; topPanel.add(chooser = new JComboBox<String>()); chooser.setToolTipText(getString("fileChooser.Tooltip")); chooser.addItemListener(new ItemListener() { Override public void itemStateChanged(ItemEvent event) { if (event.getStateChange() == ItemEvent.DESELECTED) return; String sampleName = (String) event.getItem(); boolean withFrame = isLinkBundleFrameEnabled(); if (sampleName.equals(getString("sample.CompositeLinks.Name"))) createSampleGraph(true, true, false, false, withFrame); else if (sampleName.equals(getString("sample.SimpleLinks.Name"))) createSampleGraph(false, false, false, false, withFrame); else if (sampleName.equals(getString("sample.Nested.Name"))) createSampleGraph(false, false, true, false, withFrame); else if (sampleName.equals(getString("sample.Spline.Name"))) createSampleGraph(false, false, true, true, withFrame); else throw new RuntimeException("Unsupported sample name: " + sampleName); expandedState = true; refreshExpandCollapseButton(expandCollapseButton, true); bundleFramePanel.setEnabled(true); } }); chooser.addItem(getString("sample.CompositeLinks.Name")); chooser.addItem(getString("sample.SimpleLinks.Name")); chooser.addItem(getString("sample.Nested.Name")); chooser.addItem(getString("sample.Spline.Name")); // set the component orientation according to the locale Locale loc = IlvSwingUtil.getDefaultLocale(); ComponentOrientation co = ComponentOrientation.getOrientation(loc); getContentPane().applyComponentOrientation(co); } // For debug only SuppressWarnings("unused") private String bbox(IlvGraphic obj) { IlvRect bbox = obj.boundingBox(null); return "x: min=" + bbox.x + " max=" + (bbox.x + bbox.width) + " y: min=" + bbox.y + " max=" + (bbox.y + bbox.height); } /** * Expands collapsed bundles and collapses expanded bundles. */ private void setCollapsed(final boolean collapsed) { boolean autoLayout = linkLayout.isAutoLayout(); // avoid that the link layout executes too early, before all the // bundles get expanded or collapsed linkLayout.setAutoLayout(false); applyToAllLinkBundles(grapher, new IlvApplyObject() { Override public void apply(IlvGraphic obj, Object arg) { IlvLinkBundle linkBundle = (IlvLinkBundle) obj; linkBundle.setCollapsed(collapsed); } }); // just to ensure the clipping, if any, is done towards the center of the // nodes connectLinks(); layout(linkLayout); // restore autolayout flag linkLayout.setAutoLayout(autoLayout); } /** * Refreshes the button for expanding/collapsing link bundles. */ private void refreshExpandCollapseButton(JButton button, boolean expand) { button.setText(expand ? getString("collapseButton.Label") : getString("expandButton.Label")); button.setToolTipText(expand ? getString("collapseButton.Tooltip") : getString("expandButton.Tooltip")); } /** * Enables or disables the link bundle frame. */ private void setLinkBundleFrameEnabled(final boolean enable) { linkBundleFrameEnabled = enable; applyToAllLinkBundles(grapher, new IlvApplyObject() { Override public void apply(IlvGraphic obj, Object arg) { IlvLinkBundle linkBundle = (IlvLinkBundle) obj; IlvLinkBundleFrame frame = linkBundle.getFrame(); if (enable) { if (frame == null) installLinkBundleFrame(linkBundle, (linkBundle.getGraphicBag() instanceof IlvLinkBundle) ? (subbundleFlag ? SUB_BUNDLE_FRAME_COLOR1 : SUB_BUNDLE_FRAME_COLOR2) : TOP_BUNDLE_FRAME_COLOR); // else nothing to do } else { if (frame != null) linkBundle.setFrame(null); // else nothing to do } } }); } private boolean isLinkBundleFrameEnabled() { return linkBundleFrameEnabled; } /** * Installs a frame on the link bundle. */ private void installLinkBundleFrame(IlvLinkBundle linkBundle, Color color) { IlvDefaultLinkBundleFrame defFrame = new IlvDefaultLinkBundleFrame(); color = new Color(color.getRed(), color.getGreen(), color.getBlue(), 160); defFrame.setBackground(color); defFrame.setMargin(getFrameMargin()); if (linkBundle.getOverviewLink() instanceof IlvCompositeLink) { // no expand/collapse icon at composite to avoid too complex L&F defFrame.setExpandCollapseIcon(null); } linkBundle.setFrame(defFrame); } private void setLinkStyle(int style) { if (style == this.linkStyle) return; this.linkStyle = style; switch (style) { case ORTHOGONAL_LINK_STYLE: linkLayout.setAutoLayout(true); linkLayout.setGlobalLinkStyle(IlvLinkLayout.ORTHOGONAL_STYLE); break; case DIRECT_LINK_STYLE: linkLayout.setAutoLayout(true); linkLayout.setGlobalLinkStyle(IlvLinkLayout.DIRECT_STYLE); break; case STRAIGHT_LINK_STYLE: // no link layout is needed in this style linkLayout.setAutoLayout(false); deleteIntermediateLinkPoints(false); break; default: throw new IllegalArgumentException("Unsupported style: " + style); } installLinkConnectors(); layout(linkLayout); grapher.reDraw(); } private void deleteIntermediateLinkPoints(boolean redraw) { // remove all intermediate points from all links final IlvGraphModel graphModel = linkLayout.getGraphModel(); 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 int getLinkStyle() { return linkStyle; } private void setSingleConnectionPoint(boolean single, boolean applyChange) { this.singleConnectionPoint = single; if (!applyChange) return; final boolean singleFinal = single; applyToAllLinkBundles(grapher, new IlvApplyObject() { Override public void apply(IlvGraphic obj, Object arg) { ((IlvLinkBundle) obj).setSingleConnectionPoint(singleFinal); } }); connectLinks(); layout(linkLayout); /* * For debug applyToAllLinkBundles( grapher, new IlvApplyObject() { public * void apply(IlvGraphic obj, Object arg) { IlvLinkBundle bundle = * (IlvLinkBundle)obj; IlvGraphicEnumeration sublinks = * bundle.getSublinksEnum(); int counter = 0; IlvGraphic from = * bundle.getFrom(); IlvGraphic to = bundle.getTo(); * System.out.println("From " + bbox(from)); System.out.println(" To " + * bbox(to)); IlvPoint[] bends = bundle.getLinkPoints(null); for (int i = 0; * i < bends.length; i++) { IlvPoint point = bends[i]; * System.out.println(" p[" + i + "] = " + point.x + ", " + point.y); } * while (sublinks.hasMoreElements()) { IlvLinkImage sublink = * (IlvLinkImage)sublinks.nextElement(); bends = * sublink.getLinkPoints(null); System.out.println("Sublink[" + counter++ + * "]:"); for (int i = 0; i < bends.length; i++) { IlvPoint point = * bends[i]; System.out.println(" p[" + i + "] = " + point.x + ", " + * point.y); } } } }); */ } private boolean isSingleConnectionPoint() { return singleConnectionPoint; } private void setOffset(double offset) { this.offset = offset; } private double getOffset() { return offset; } private void setFrameMargin(double frameMargin) { this.frameMargin = frameMargin; } private double getFrameMargin() { return frameMargin; } /** * Add a toggle button to the input component. */ JToggleButton addButton(JComponent component, String imageName, String toolTip, boolean initialSelected, ActionListener action) { JToggleButton b = new JToggleButton() { Override public Dimension getPreferredSize() { return new Dimension(26, 26); } Override public Dimension getMinimumSize() { return new Dimension(26, 26); } Override public Dimension getMaximumSize() { return new Dimension(26, 26); } }; Image image = getImageFromFile(imageName); if (image != null) b.setIcon(new ImageIcon(image)); b.setToolTipText(toolTip); b.setSelected(initialSelected); b.addActionListener(action); component.add(b); return b; } private Image getImageFromFile(String imageName) { try { return IlvImageUtil.getImageFromFile(LinkBundleApp.class, "/gif/" + imageName); } catch (Exception ex) { ex.printStackTrace(); // System.err.println("Warning: error during read for image " + // imageName); return null; } } /** * Applies a change recursivelly to all <code>IlvLinkBundle</code> objects * contained in <code>grapher</code>. */ private void applyToAllLinkBundles(IlvGrapher graph, IlvApplyObject apply) { applyToAllLinkBundles(graph, apply, true); } /** * Applies a change to all <code>IlvLinkBundle</code> objects contained in * <code>grapher</code>. */ private void applyToAllLinkBundles(IlvGraphicBag bag, IlvApplyObject apply, boolean recursive) { IlvGraphicEnumeration objects = bag.getObjects(); while (objects.hasMoreElements()) { IlvGraphic obj = objects.nextElement(); if (obj instanceof IlvLinkBundle) { // Apply on children first, because when applying to // a given link bundle the children we want the children // to be already processed. E.g. when switching on/off // the link bundle frame, the layout of a given bundle // needs to take into account the frame of the nested bundles. if (recursive) { IlvLinkBundle linkBundle = (IlvLinkBundle) obj; applyToAllLinkBundles(linkBundle, apply, recursive); } bag.applyToObject(obj, apply, null, false); } } if (bag instanceof IlvGrapher) ((IlvGrapher) bag).reDraw(); } /** * Installs the appropriate link connectors on each link contained in * <code>grapher</code>. */ private void installAppropriateLinkConnector(IlvGrapher grapher) { int clippingMode = -1; if (isSingleConnectionPoint()) { if (withNestedBundles || (getLinkStyle() == STRAIGHT_LINK_STYLE)) clippingMode = CLIPPING_LINK_CONNECTOR_ON_NODES_AND_FREE_ON_SUBLINKS; else clippingMode = CLIPPING_LINK_CONNECTOR_ON_NODES; } else if (getLinkStyle() == STRAIGHT_LINK_STYLE) clippingMode = CLIPPING_LINK_CONNECTOR_ON_NODES; else clippingMode = FREE_LINK_CONNECTOR_ON_NODES; installAppropriateLinkConnector(grapher, grapher, clippingMode); } private void installAppropriateLinkConnector(IlvGraphicBag bag, IlvGrapher topLevelGrapher, int clippingMode) { if (bag == null) return; IlvGraphicEnumeration objects = bag.getObjects(); while (objects.hasMoreElements()) { IlvGraphic obj = objects.nextElement(); if (obj instanceof IlvLinkBundle) { IlvLinkBundle linkBundle = (IlvLinkBundle) obj; // IlvLinkImage overview = linkBundle.getOverviewLink(); boolean freeConnector = clippingMode == CLIPPING_LINK_CONNECTOR_ON_NODES_AND_FREE_ON_SUBLINKS && bag != topLevelGrapher; // children BEFORE parent installAppropriateLinkConnector(linkBundle, topLevelGrapher, clippingMode); installAppropriateLinkConnector(linkBundle, freeConnector); linkBundle.invalidateLayout(); } else if (obj instanceof IlvLinkImage) { boolean freeConnector = clippingMode == CLIPPING_LINK_CONNECTOR_ON_NODES_AND_FREE_ON_SUBLINKS && bag != topLevelGrapher; installAppropriatePerLinkLinkConnector((IlvLinkImage) obj, freeConnector); if (obj instanceof IlvCompositeLink) { IlvLinkImage overview = ((IlvCompositeLink) obj).getLink(); if (overview != null) installAppropriatePerLinkLinkConnector(overview, freeConnector); } } else // it's a node installAppropriateNodeLinkConnector(obj, clippingMode); } if (bag instanceof IlvGrapher) ((IlvGrapher) bag).reDraw(); } private void installAppropriateLinkConnector(IlvLinkBundle linkBundle, boolean freeConnector) { IlvLinkImage overview = linkBundle.getOverviewLink(); installAppropriatePerLinkLinkConnector(linkBundle, freeConnector); if (overview != null) installAppropriatePerLinkLinkConnector(overview, freeConnector); } private void installAppropriatePerLinkLinkConnector(IlvLinkImage link, boolean freeConnector) { installAppropriatePerLinkLinkConnector(link, freeConnector, true); installAppropriatePerLinkLinkConnector(link, freeConnector, false); } private void installAppropriatePerLinkLinkConnector(IlvLinkImage link, boolean freeConnector, boolean origin) { IlvLinkConnector lc = IlvLinkConnector.GetAttached(link, origin); if (freeConnector) { if (lc == null || !(lc instanceof IlvFreeLinkConnector) || (lc instanceof IlvClippingLinkConnector)) new IlvFreeLinkConnector(link, origin); } else { if (lc != null) lc.detach(link, origin, false/* redraw */); } } private void installAppropriateNodeLinkConnector(IlvGraphic node, int clippingMode) { IlvLinkConnector lc = IlvLinkConnector.GetAttached(node); switch (clippingMode) { case FREE_LINK_CONNECTOR_ON_NODES: if (lc == null || (!(lc instanceof IlvFreeLinkConnector) || (lc instanceof IlvClippingLinkConnector))) new IlvFreeLinkConnector(node); break; case CLIPPING_LINK_CONNECTOR_ON_NODES: case CLIPPING_LINK_CONNECTOR_ON_NODES_AND_FREE_ON_SUBLINKS: // for both clipping modes: if (lc == null || !(lc instanceof IlvClippingLinkConnector)) new IlvClippingLinkConnector(node); break; default: // nothing } } /** * 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() { LinkBundleApp app = new LinkBundleApp(); app.init(); JFrame frame = new JFrame(getString("Frame.Label")); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(550, 650); frame.getContentPane().add(app); frame.setVisible(true); } }); } /** * Installs the link connectors on all nodes. */ private void installLinkConnectors() { applyToAllNodes(grapher, new IlvApplyObject() { Override public void apply(IlvGraphic obj, Object arg) { installLinkConnector(obj); } }); connectLinks(); } private void connectLinks() { // Installing or reinstalling the link connectors requires // the internal layout of each bundle to be redone, hence: applyToAllLinkBundles(grapher, new IlvApplyObject() { Override public void apply(IlvGraphic obj, Object arg) { IlvLinkBundle linkBundle = (IlvLinkBundle) obj; connectLink(linkBundle); linkBundle.invalidateLayout(); } }); installAppropriateLinkConnector(grapher); } /** * Installs the appropriate link connectors on a node. */ private void installLinkConnector(IlvGraphic node) { if (linkStyle == STRAIGHT_LINK_STYLE) new IlvClippingLinkConnector(node); else new IlvFreeLinkConnector(node); } /** * Applies something to all nodes. */ private void applyToAllNodes(IlvGrapher graph, IlvApplyObject f) { IlvGraphicEnumeration e = graph.getObjects(); IlvGraphic g; while (e.hasMoreElements()) { g = e.nextElement(); if (graph.isNode(g)) graph.applyToObject(g, f, null, true); } } /** * Performs the layout of the graph. */ private void layout(IlvGraphLayout layout) { if (getLinkStyle() == STRAIGHT_LINK_STYLE) return; // nothing to do in this case try { // perform the layout layout.performLayout(false, true); } catch (IlvGraphLayoutException ex) { ex.printStackTrace(); } } private IlvCompositeLink createCompositeLink(IlvGraphic origin, IlvGraphic dest, IlvLinkImage overviewLink, String label, double lineWidth, Color color) { IlvCompositeLink clink = new IlvCompositeLink(origin, dest, false); if (overviewLink != null) clink.setLink(overviewLink); clink.setLineWidth(lineWidth); if (color != null) clink.setForeground(color); clink.setEndCap(IlvStroke.CAP_BUTT); clink.setLineJoin(IlvStroke.JOIN_ROUND); // grapher.addLink(clink,false); // Creates a plinth rectangle with a label IlvCompositeGraphic plinth = new IlvCompositeGraphic(); plinth.setLayout(new IlvCenteredLayout(new Insets(4, 10, 4, 10))); IlvRectangle rect = new IlvReliefRectangle(new IlvRect(0, 0, 40, 40)); rect.setBackground(Color.red); rect.setToolTipText(getString("rect.Tooltip")); plinth.setChildren(0, rect); if (label != null) { IlvZoomableLabel name = new IlvZoomableLabel(new IlvPoint(), label, false); plinth.setChildren(1, name); } // Adds the plinth to the link clink.setChildren(0, plinth); clink.setConstraints(0, new IlvAttachmentConstraint(IlvAttachmentLocation.CenterPostOrderFirst, IlvLinkAttachmentLocation.MiddleLink)); // Creates a stack of small rectangles IlvCompositeGraphic stacker = new IlvCompositeGraphic(); stacker.setLayout(new IlvStackerLayout()); // Creates a tiny blue rectangle IlvRectangle r1 = new IlvRectangle(new IlvRect(0, 0, 10, 10)); r1.setToolTipText(getString("r1.Tooltip")); r1.setBackground(Color.blue); r1.setForeground(Color.blue); r1.setFillOn(true); stacker.setChildren(0, r1); // Creates a tiny red rectangle IlvRectangle r2 = new IlvRectangle(new IlvRect(0, 0, 10, 10)); r2.setBackground(Color.red); r2.setForeground(Color.red); r2.setFillOn(true); stacker.setChildren(1, r2); // Creates a tiny green rectangle IlvRectangle r3 = new IlvRectangle(new IlvRect(0, 0, 10, 10)); r3.setBackground(Color.green); r3.setForeground(Color.green); r3.setFillOn(true); stacker.setChildren(2, r3); // Adds the stack of rectangles clink.setChildren(1, stacker); // IlvLinkAttachmentLocation.NearFromLink --> hotSpot, // IlvLinkAttachmentLocation.FromLink --> anchor clink.setConstraints(1, new IlvAttachmentConstraint(IlvLinkAttachmentLocation.NearFromLink, IlvLinkAttachmentLocation.FromLink)); IlvIcon ad_hoc = new IlvIcon(getImageFromFile("ad-hoc-marker.gif"), new IlvRect(0, 0, 16, 7)); clink.setChildren(2, ad_hoc); clink.setConstraints(2, new IlvAttachmentConstraint(IlvLinkAttachmentLocation.NearToLink, IlvLinkAttachmentLocation.ToLink)); return clink; } /** * Creates the sample graph containing link bundles. * * @param withCompositeSublinks * If true, composite links are used for some sublinks. * @param withCompositeOverview * If true, composite links are used for the overview links of the * link bundles. */ private void createSampleGraph(boolean withCompositeSublinks, boolean withCompositeOverview, boolean withNestedBundles, boolean withSplines, boolean withFrame) { this.withNestedBundles = withNestedBundles; // Clean the existing content (if any) grapher.deleteAll(false); // Creates the nodes IlvGraphic node1 = createNode(50, 50); IlvGraphic node2 = createNode(350, 50); IlvGraphic node3 = createNode(300, 300); // Add the nodes to the grapher grapher.addNode(node1, NODE_LAYER, false); grapher.addNode(node2, NODE_LAYER, false); grapher.addNode(node3, NODE_LAYER, false); // Create the link bundles IlvLinkBundle linkBundle1 = createLinkBundle(node1, node2, withCompositeSublinks, withCompositeOverview, withNestedBundles ? N_SUBLINKS_IN_SUB_BUNDLE : N_SUBLINKS_IN_TOP_BUNDLE, withNestedBundles, withSplines, withFrame, TOP_BUNDLE_FRAME_COLOR); IlvLinkBundle linkBundle2 = createLinkBundle(node2, node3, withCompositeSublinks, withCompositeOverview, N_SUBLINKS_IN_TOP_BUNDLE, false, withSplines, withFrame, TOP_BUNDLE_FRAME_COLOR); IlvLinkBundle linkBundle3 = createLinkBundle(node1, node3, withCompositeSublinks, withCompositeOverview, withNestedBundles ? N_SUBLINKS_IN_SUB_BUNDLE : N_SUBLINKS_IN_TOP_BUNDLE, withNestedBundles, withSplines, withFrame, TOP_BUNDLE_FRAME_COLOR); // Add the link bundles to the grapher grapher.addLink(linkBundle1, LINK_LAYER, false); grapher.addLink(linkBundle2, LINK_LAYER, false); grapher.addLink(linkBundle3, LINK_LAYER, false); grapher.reDraw(); connectLinks(); } private IlvLinkBundle createLinkBundle(IlvGraphic origin, IlvGraphic dest, boolean withCompositeSublinks, boolean withCompositeOverview, int nSublinks, boolean withNestedBundles, boolean withSplines, boolean withFrame, Color frameColor) { // Creates the link responsible for the rendering IlvLinkImage overviewLink = createOverviewLink(origin, dest, withCompositeOverview, withSplines); overviewLink.setPopupMenuName("EXPAND_BUNDLE"); IlvLinkBundle linkBundle = new IlvLinkBundle(origin, dest, overviewLink.isOriented(), null, overviewLink); linkBundle.setToolTipText(getString("linkBundle.Tooltip")); if (withFrame) installLinkBundleFrame(linkBundle, frameColor); linkBundle.setPopupMenuName("COLLAPSE_BUNDLE"); linkBundle.setOffset(getOffset()); linkBundle.setSingleConnectionPoint(isSingleConnectionPoint()); // ------------- IlvGraphicVector vectLinks = new IlvGraphicVector(); IlvLinkImage sublink; for (int i = 0; i < nSublinks; i++) { // if appropriate, create a composite link for half // of the sublinks sublink = withNestedBundles ? createLinkBundle(origin, dest, withCompositeSublinks, withCompositeOverview, N_SUBLINKS_IN_SUB_BUNDLE, false, withSplines, withFrame, (i == 0 ? SUB_BUNDLE_FRAME_COLOR1 : SUB_BUNDLE_FRAME_COLOR2)) : createSubLink(origin, dest, withCompositeSublinks && i % 2 == 1, withSplines, SUBLINKS_WIDTH); vectLinks.addElement(sublink); Object[] args = { Integer.valueOf(i) }; sublink.setToolTipText(MessageFormat.format(getString("sublink.Tooltip"), args)); linkBundle.addSublink(sublink); connectLink(sublink); } connectLink(overviewLink); return linkBundle; } private IlvLinkImage createOverviewLink(IlvGraphic origin, IlvGraphic dest, boolean composite, boolean spline) { IlvLinkImage overviewLink = createOverviewLink(origin, dest, false, spline, OVERVIEW_LINK_WIDTH); if (composite) overviewLink = createCompositeLink(origin, dest, overviewLink, "Overview link", OVERVIEW_LINK_WIDTH, OVERVIEW_LINK_COLOR); // overviewLink.setToolTipText("Overview link"); return overviewLink; } private IlvLinkImage createSubLink(IlvGraphic origin, IlvGraphic dest, boolean composite, boolean spline, double lineWidth) { return createLink(origin, dest, composite, spline, lineWidth, SUBLINK_COLOR); } private IlvLinkImage createOverviewLink(IlvGraphic origin, IlvGraphic dest, boolean composite, boolean spline, double lineWidth) { return createLink(origin, dest, composite, spline, lineWidth, OVERVIEW_LINK_COLOR); } private IlvLinkImage createLink(IlvGraphic origin, IlvGraphic dest, boolean composite, boolean spline, double lineWidth, Color color) { IlvLinkImage link; if (spline) { link = new IlvSplineLinkImage(origin, dest, false, null); } else { link = new IlvPolylineLinkImage(origin, dest, false, null); } if (composite) { link = createCompositeLink(origin, dest, link, " ", // null, // "Sublink", SUBLINKS_WIDTH, color); } link.setLineWidth(lineWidth); link.setEndCap(IlvStroke.CAP_BUTT); link.setLineJoin(IlvStroke.JOIN_ROUND); if (color != null) link.setForeground(color); return link; } private void connectLink(IlvLinkImage link) { // recurse inside bundles if (link instanceof IlvLinkBundle) { IlvGraphicEnumeration sublinks = ((IlvLinkBundle) link).getSublinksEnum(); while (sublinks.hasMoreElements()) connectLink((IlvLinkImage) sublinks.nextElement()); } if (link instanceof IlvCompositeLink) { IlvLinkImage overviewLink = ((IlvCompositeLink) link).getLink(); connectLink(overviewLink); } connectLink(IlvLinkConnector.Get(link, true), IlvLinkConnector.Get(link, false), link, link.getFrom().boundingBox(null), link.getTo().boundingBox(null)); } private void connectLink(IlvLinkConnector lcFrom, IlvLinkConnector lcTo, IlvLinkImage link, IlvRect bboxOrigin, IlvRect bboxDest) { if (lcFrom != null) lcFrom.connectLink(link, new IlvPoint(bboxOrigin.x + bboxOrigin.width * 0.5, bboxOrigin.y + bboxOrigin.height * 0.5), true, null); if (lcTo != null) lcTo.connectLink(link, new IlvPoint(bboxDest.x + bboxDest.width * 0.5, bboxDest.y + bboxDest.height * 0.5), true, null); } private IlvGraphic createNode(double x, double y) { IlvGraphic node = new IlvReliefRectangle(new IlvRect(x, y, NODE_WIDTH, NODE_HEIGHT)); node.setBackground(NODE_COLOR); node.setToolTipText(getString("node.Tooltip")); installLinkConnector(node); return node; } 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(getString("collapseButton.Label"))) { if (graphic instanceof IlvLinkBundle) { IlvLinkBundle bundle = (IlvLinkBundle) graphic; if (bundle.getGraphicBag() != null) { bundle.getGraphicBag().applyToObject(bundle, new IlvApplyObject() { Override public void apply(IlvGraphic g, Object arg) { ((IlvLinkBundle) g).setCollapsed(true); } }, null, true); } else { bundle.setCollapsed(true); } } } if (m.getText().equals(getString("expandButton.Label"))) { // 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; if (bundle.getGraphicBag() != null) { bundle.getGraphicBag().applyToObject(bundle, new IlvApplyObject() { Override public void apply(IlvGraphic g, Object arg) { ((IlvLinkBundle) g).setCollapsed(false); } }, null, true); } else { bundle.setCollapsed(false); } } } } }; IlvSimplePopupMenu menu = new IlvSimplePopupMenu("Menu1", collapse ? getString("collapseButton.Label") : getString("expandButton.Label"), 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; } /** * Returns a string. */ static String getString(String key) { return IlvResourceUtil.getString(key, LinkBundleApp.class, IlvLocaleUtil.getCurrentLocale()); } 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); } } }