/*
* Licensed Materials - Property of Rogue Wave Software, Inc.
* © Copyright Rogue Wave Software, Inc. 2014, 2017
* © 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 = { 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,
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);
}
}
}