/* * Licensed Materials - Property of Rogue Wave Software, Inc. * © Copyright Rogue Wave Software, Inc. 2014, 2015 * © Copyright IBM Corp. 2009, 2014 * © Copyright ILOG 1996, 2009 * All Rights Reserved. * * Note to U.S. Government Users Restricted Rights: * The Software and Documentation were developed at private expense and * are "Commercial Items" as that term is defined at 48 CFR 2.101, * consisting of "Commercial Computer Software" and * "Commercial Computer Software Documentation", as such terms are * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4, * as applicable. */ import ilog.views.*; import ilog.views.interactor.*; import ilog.views.graphic.*; import ilog.views.graphic.linkbundle.IlvLinkBundle; import ilog.views.graphic.linkbundle.IlvLinkBundleFrame; import ilog.views.graphic.linkbundle.IlvDefaultLinkBundleFrame; import ilog.views.graphic.composite.*; import ilog.views.graphic.composite.layout.*; import ilog.views.swing.*; import ilog.views.util.IlvImageUtil; import ilog.views.util.IlvProductUtil; import ilog.views.util.IlvResourceUtil; import ilog.views.util.IlvLocaleUtil; import ilog.views.util.swing.IlvSwingUtil; import ilog.views.util.swing.IlvJSpinnerDecimalNumberField; import ilog.views.linkconnector.*; import ilog.views.graphlayout.*; import ilog.views.graphlayout.link.*; import java.awt.*; import java.awt.event.*; import java.util.*; import java.text.*; import javax.swing.*; /** * The main class LinkBundleApplet. This demonstrates the class * <code>ilog.views.graphic.linkbundle.IlvLinkBundle</code>. */ public class LinkBundleApplet extends JApplet { static { // This applet is designed to run only with default resource bundle // and various selected other resource bundles. // Setting the available resource suffixes avoids that the applet // tries to load resource bundles for other locales over the net, // even if the current locale of the browser is different. if (IlvResourceUtil.isInApplet()) IlvResourceUtil.setAvailableResourceSuffixes("", "_ja"); // 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 float offset = 5f; private float frameMargin = 3f; private boolean expandedState = true; private boolean subbundleFlag = true; private static final float NODE_WIDTH = 100; private static final float NODE_HEIGHT = 100; private static final float SUBLINKS_WIDTH = 5; private static final float 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 applet/application. */ public void init() { super.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) { 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 linkStyleChooser = new JComboBox(); 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() { public void itemStateChanged(ItemEvent event) { if (event.getStateChange() == event.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() { 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() { public void actionPerformed(ActionEvent event) { final float value = (float)offsetField.getDoubleValue(); setOffset(value); applyToAllLinkBundles( grapher, new IlvApplyObject() { 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() { public void actionPerformed(ActionEvent event) { final float value = (float)marginField.getDoubleValue(); setFrameMargin(value); applyToAllLinkBundles( grapher, new IlvApplyObject() { 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() { 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() { 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 chooser; topPanel.add(chooser = new JComboBox()); chooser.setToolTipText(getString("fileChooser.Tooltip")); chooser.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent event) { if (event.getStateChange() == event.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); } /** * Called when this applet is being reclaimed in order to destroy * any resources that it has allocated. */ public void destroy() { super.destroy(); // This method is intended to workaround memory management issues // in the Sun JRE. Please refer to the method documentation for more // details and a description of the known issues. IlvSwingUtil.cleanupApplet(); } // For debug only 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() { 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() { 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() { 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(float offset) { this.offset = offset; } private float getOffset() { return offset; } private void setFrameMargin(float frameMargin) { this.frameMargin = frameMargin; } private float 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() { public Dimension getPreferredSize () { return new Dimension(26,26); } public Dimension getMinimumSize () { return new Dimension(26,26); } 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(LinkBundleApplet.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() { public void run() { LinkBundleApplet applet = new LinkBundleApplet(); applet.init(); JFrame frame = new JFrame(getString("Frame.Label")); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(550, 650); frame.getContentPane().add(applet); frame.setVisible(true); } }); } /** * Installs the link connectors on all nodes. */ private void installLinkConnectors() { applyToAllNodes( grapher, new IlvApplyObject() { 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() { 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, float 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 = { new Integer(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, float lineWidth) { return createLink(origin, dest, composite, spline, lineWidth, SUBLINK_COLOR); } private IlvLinkImage createOverviewLink(IlvGraphic origin, IlvGraphic dest, boolean composite, boolean spline, float lineWidth) { return createLink(origin, dest, composite, spline, lineWidth, OVERVIEW_LINK_COLOR); } private IlvLinkImage createLink(IlvGraphic origin, IlvGraphic dest, boolean composite, boolean spline, float 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.5f, bboxOrigin.y + bboxOrigin.height*0.5f), true, null); if (lcTo != null) lcTo.connectLink(link, new IlvPoint(bboxDest.x + bboxDest.width*0.5f, bboxDest.y + bboxDest.height*0.5f), true, null); } private IlvGraphic createNode(float x, float 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() { 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() { 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() { 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 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, LinkBundleApplet.class, IlvLocaleUtil.getCurrentLocale()); } private static class JComfortPanel extends JPanel { public void setEnabled(boolean enabled) { Component[] components = getComponents(); for (int i = 0; i < components.length; i++) components[i].setEnabled(enabled); } } }