/*
* 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.
*/
package demolayoutgallery;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Point2D;
import java.net.URL;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import demolayoutgallery.base.Ellipse;
import demolayoutgallery.base.HelpHandler;
import demolayoutgallery.base.ImageUtil;
import demolayoutgallery.base.LayoutApp;
import demolayoutgallery.base.Link;
import demolayoutgallery.base.ManagerView;
import demolayoutgallery.base.OverviewHandler;
import demolayoutgallery.base.ScrollHTMLTextArea;
import demolayoutgallery.base.SplineLink;
import ilog.views.IlvApplyObject;
import ilog.views.IlvGrapher;
import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.IlvGraphicVector;
import ilog.views.IlvLinkConnector;
import ilog.views.IlvLinkImage;
import ilog.views.IlvManager;
import ilog.views.IlvManagerView;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvTransformer;
import ilog.views.animation.IlvManagerAnimator;
import ilog.views.event.ManagerSelectionChangedEvent;
import ilog.views.graphic.IlvReliefRectangle;
import ilog.views.graphic.IlvSplineLinkImage;
import ilog.views.graphic.IlvZoomableLabel;
import ilog.views.graphlayout.IlvGraphLayout;
import ilog.views.graphlayout.IlvGrapherAdapter;
import ilog.views.graphlayout.bus.IlvBusLayout;
import ilog.views.graphlayout.circular.IlvCircularLayout;
import ilog.views.graphlayout.grid.IlvGridLayout;
import ilog.views.graphlayout.hierarchical.IlvHierarchicalLayout;
import ilog.views.graphlayout.link.IlvLinkLayout;
import ilog.views.graphlayout.topologicalmesh.IlvTopologicalMeshLayout;
import ilog.views.graphlayout.tree.IlvTreeLayout;
import ilog.views.graphlayout.uniformlengthedges.IlvUniformLengthEdgesLayout;
import ilog.views.interactor.IlvSelectInteractor;
import ilog.views.interactor.IlvZoomViewInteractor;
import ilog.views.linkconnector.IlvClippingLinkConnector;
import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.util.java2d.IlvMultipleGradientPaint;
import ilog.views.util.java2d.IlvRadialGradientPaint;
/**
* The main class IlvLayoutGalleryApp. It demonstrates the graph layout
* functionality provided in JViews. It also demonstrates the support for nested
* graphs, the labeling algorithm, and the intergraph link routing mechanism.
*/
public final class IlvLayoutGalleryApp extends LayoutApp {
// --------------------------------------------------------------------------
// Constants
// The property name to store the background color of nodes temporarily
private static final String BACKGROUND_COLOR = "__BackgroundColor";
// if true, company's logo is added to the toolbar
private static final boolean WITH_LOGO = false;
// if true, the demo starts in self-running mode.
// If true, the "delay" argument passed on the command line is
// used. However, if no delay argument is passed, the default
// delay = 1500 ms is used.
private static final boolean SELF_RUNNING = false;
// the default sizes of various windows (for the main window, this is
// used only if STANDALONE_MAIN_FRAME is true)
private static final int MAIN_WINDOW_DEFAULT_WIDTH = WITH_LOGO ? 750 : 750;
private static final int MAIN_WINDOW_DEFAULT_HEIGHT = WITH_LOGO ? 700 : 700;
private static final int HELP_WINDOW_DEFAULT_WIDTH = 500;
private static final int HELP_WINDOW_DEFAULT_HEIGHT = 300;
private static final int OVERVIEW_MIN_SIZE = 100;
private static final int OVERVIEW_MAX_SIZE = 170;
private static final int TEXT_AREA_HEIGHT = 80;
// the path to the images of the buttons. Notice this is
// different than the path used by the parent class.
private static final String PATH_TO_LOCAL_BUTTON_IMAGES = "gif/";
// the interaction mode
private static final int SELECT_MODE = 1;
private static final int EXPAND_MODE = 2;
private static final int ZOOMBOX_MODE = 3;
// --------------------------------------------------------------------------
// Data members related to the graphs
// whether this is the very first reading of a graph
private boolean firstTimeRead = true;
// the grapher
private IlvGrapher grapher;
// The corresponding scroll manager view
private IlvJScrollManagerView scrollManView;
// we need to handle the autolayout of link layout at various places,
// therefore faster access to the link layout.
private IlvLinkLayout linkLayout;
// The set of layouts
private LayoutDescriptor[] layoutDescriptions;
// the currently selected layout
private IlvGraphLayout currentLayout;
// the base name (without directory nor extension) of the
// file containing the currently loaded graph
private String currentGraphFileBaseName;
// The link clip provider for link clipping
private LinkClipProvider linkClipProvider = new LinkClipProvider();
// the grapher animator that performs the animation
IlvManagerAnimator mgranim;
private AbstractGraphLayoutSupport flatGraphDemoSupport;
private AbstractGraphLayoutSupport nestedGraphDemoSupport;
// The number of nodes and links in the grapher
private int numberOfNodes = 0;
private int numberOfLinks = 0;
private int numberOfSubgraphs = 0;
// --------------------------------------------------------------------------
// The demo support class
private AbstractGraphLayoutSupport layoutSupport;
// --------------------------------------------------------------------------
// Data members related to the GUI
// the GUI has 3 parts: a top panel, a bottom panel, and in the middle the
// scroll manager view.
// the main frame
private JFrame mainFrame;
// the main container
private Container mainContainer;
// the top and bottom panel
private JPanel topPanel;
private JPanel bottomPanel;
// the interactor buttons
private AbstractButton layoutButton, randomizeButton;
private AbstractButton selectButton, expandButton;
private AbstractButton overviewButton;
private AbstractButton animationButton, helpButton;
// the interactor for the expandButton
private ExpandInteractor expandInteractor;
// the cursor for the expand operation
private Cursor expandIdleCursor;
private Cursor expandWorkCursor;
// the chooser for layout and graph size
private JComboBox<String> sizeChooser, layoutChooser;
// the overview handler
public OverviewHandler overviewHandler;
// the help window handler
public HelpHandler helpHandler;
// flags to be able to restore correctly the visibility when the app
// restarts after having been stopped
private boolean helpVisibleWhenStop = true;
private boolean overviewVisibleWhenStop = true;
// the text area to show messages
private ScrollHTMLTextArea textArea;
// the image utilities
private ImageUtil imageUtil;
// --------------------------------------------------------------------------
// Data members related to node shapes
// constants for the node shapes
private static final int RECTANGLE_NODE_SHAPE = 1;
static final int SPHERE_NODE_SHAPE = 2;
// standard size of terminal and rectangle nodes
private IlvRect standardNodeRect = new IlvRect(0, 0, 35, 39);
// standard size of sphere nodes
private IlvRect standardSphereNodeRect = new IlvRect(0, 0, 26, 26);
// the standard color
private Color standardColor = new Color(255, 180, 180);
// the selected and the current node shape
private int nodeShape = SPHERE_NODE_SHAPE;
private int currentNodeShape = nodeShape;
// whether we have the choice of the node shapes. Some sample graphs are
// not suitable for any node shape
private boolean noNodeShapeChoice = false;
// --------------------------------------------------------------------------
// Further flags
// whether layout is running
private boolean layoutRunning = false;
// whether the GUI is enabled
private boolean isGUIenabled = true;
// whether the graph can use animation
private boolean animationAllowed = true;
private boolean animation = true;
// whether the graph can be randomized
private boolean randomizeAllowed = true;
// whether the layout can run automatic after layout
private boolean automaticLayoutRun = true;
// whether the demo is in autoplay mode
private boolean autoPlay = false;
// the delay (in millisec) after each automatic run in autoplay mode
private long autoPlayDelay = 4000;
// whether the help window should be shown at startup
private static final boolean HELP_AT_START_UP = false;
// --------------------------------------------------------------------------
// Temporary data
// temp. store whether link layout was in autolayout mode
private boolean autoLayout = false;
// temp. storage of the old cursor
private Cursor oldCursor;
// --------------------------------------------------------------------------
// The real entry methods for this application
/**
* Initializes the demo.
*/
Override
public Container initDemo() {
initLayouts();
// create an image utitity for the image operations
imageUtil = new ImageUtil(this);
// local initialziation
initLocal();
// create and open the main window frame
createMainWindow();
// read the graph from the file
if (!isAutoPlay()) {
readGraph();
}
// show help
if (firstTimeRead && HELP_AT_START_UP) {
helpButton.setSelected(true);
helpHandler.toggleHelp(true);
}
firstTimeRead = false;
if (!isAutoPlay()) {
// if necessary, perform layout
if (isAutomaticLayoutRun())
arrange(currentLayout, grapher, false);
}
// to avoid that the main window is behind the help window
if (mainFrame != null)
mainFrame.toFront();
// if automatic play is required, do so
if (isAutoPlay())
autoPlay();
return mainFrame != null ? mainFrame : mainContainer;
}
/**
* Stops the application.
*/
Override
public void stop() {
super.stop();
// register the visibility of the help and overview frames,
// to be able to restore them correctly if the app is restarted later.
helpVisibleWhenStop =
helpHandler != null && helpHandler.getFrame() != null && helpHandler.getFrame().isVisible();
overviewVisibleWhenStop = overviewHandler != null && overviewHandler.getFrame() != null
&& overviewHandler.getFrame().isVisible();
setVisibleFrames(false, true);
}
/**
* Start the application.
*/
Override
public void start() {
super.stop();
setVisibleFrames(true, false);
}
private void setVisibleFrames(boolean visible, boolean force) {
if (helpHandler != null && helpHandler.getFrame() != null
&& (force || !(visible && !helpVisibleWhenStop)) && !(visible && !HELP_AT_START_UP))
setVisible(helpHandler.getFrame(), visible);
if (overviewHandler != null && overviewHandler.getFrame() != null
&& (force || !(visible && !overviewVisibleWhenStop)))
setVisible(overviewHandler.getFrame(), visible);
if (mainFrame != null)
setVisible(mainFrame, visible);
}
/**
* Initializes the layouts.
*/
private void initLayouts() {
// Some settings on the layout algorithms:
// the options for the tree layout that runs in radial mode
IlvTreeLayout radialTreeLayout = new IlvTreeLayout();
radialTreeLayout.setLayoutMode(IlvTreeLayout.RADIAL);
radialTreeLayout.setSiblingOffset(20);
radialTreeLayout.setBranchOffset(20);
radialTreeLayout.setParentChildOffset(20);
// increase a little the preferred link length
IlvUniformLengthEdgesLayout ule = new IlvUniformLengthEdgesLayout();
ule.setPreferredLinksLength(50);
ule.setRespectNodeSizes(true);
linkLayout = new IlvLinkLayout();
// the link style for the orthogonal
linkLayout.setGlobalLinkStyle(IlvLinkLayout.ORTHOGONAL_STYLE);
linkLayout.setAutoLayout(false);
layoutDescriptions = new LayoutDescriptor[12];
int i = 0;
layoutDescriptions[i++] = new LayoutDescriptor("Flow Chart", "flow-chart");
layoutDescriptions[i++] = new LayoutDescriptor("Mixed Nesting", "mixed-nesting");
layoutDescriptions[i++] = new LayoutDescriptor("Nested Tree", "nested-tree");
layoutDescriptions[i++] =
new LayoutDescriptor(new IlvHierarchicalLayout(), "Hierarchical", "hierarchical");
layoutDescriptions[i++] = new LayoutDescriptor(new IlvTreeLayout(), "Tree", "tree");
layoutDescriptions[i++] = new LayoutDescriptor(radialTreeLayout, "Radial Tree", "radial");
layoutDescriptions[i++] = new LayoutDescriptor(ule, "Uniform Length Edges", "uniform");
layoutDescriptions[i++] = new LayoutDescriptor(new IlvTopologicalMeshLayout(), "Mesh", "mesh");
layoutDescriptions[i++] = new LayoutDescriptor(linkLayout, "Link Routing", "link");
layoutDescriptions[i++] = new LayoutDescriptor(new IlvBusLayout(), "Bus", "bus");
layoutDescriptions[i++] =
new LayoutDescriptor(new IlvCircularLayout(), "Circular (Ring/Star)", "circular");
layoutDescriptions[i++] = new LayoutDescriptor(new IlvGridLayout(), "Grid", "grid");
}
/**
* Starts the demo as a Java application.
*/
public static final void main(final String[] arg) {
// Sun recommends that to put the entire GUI initialization into the
// AWT thread
SwingUtilities.invokeLater(new Runnable() {
Override
public void run() {
final IlvLayoutGalleryApp layoutDemo = new IlvLayoutGalleryApp();
long delay = -1;
if (arg.length == 1) {
// Passing the delay argument is interpreted as a request for auto
// play
try {
delay = Math.max(0, Long.parseLong(arg[0]));
} catch (NumberFormatException ex) {
System.err.println("bad command line argument: " + arg[0]);
System.exit(0);
}
} else if (SELF_RUNNING)
delay = 1500; // default value (ms)
if (delay >= 0) {
layoutDemo.setAutoPlay(true);
layoutDemo.setAutoPlayDelay(delay);
}
layoutDemo.init();
if (!layoutDemo.isAutoPlay() && layoutDemo.mainFrame != null)
setVisible(layoutDemo.mainFrame, true);
}
});
}
// --------------------------------------------------------------------------
// Accessors
/**
* Returns the top-level grapher.
*/
public IlvGrapher getGrapher() {
return grapher;
}
/**
* Returns the manager view.
*/
public IlvManagerView getManagerView() {
return mgrview;
}
/**
* Returns the scroll manager view.
*/
public IlvJScrollManagerView getScrollManagerView() {
return scrollManView;
}
/**
* Returns the manager animator.
*/
public IlvManagerAnimator getManagerAnimator() {
return mgranim;
}
/**
* Sets the "layoutRunning" flag.
*/
public void setLayoutRunning(boolean running) {
layoutRunning = running;
}
/**
* Returns <code>true</code> if the current layout is currently running, and
* <code>false</code> otherwise.
*/
Override
public boolean isLayoutRunning() {
return layoutRunning;
}
/**
* Returns the link layout.
*/
public IlvLinkLayout getLinklayout() {
return linkLayout;
}
/**
* Returns the currently selected layout.
*/
public IlvGraphLayout getCurrentLayout() {
return currentLayout;
}
/**
* Returns the base name (without directory nor extension) of the file
* containing the currently loaded graph.
*/
public String getCurrentGraphFileBaseName() {
return currentGraphFileBaseName;
}
/**
* Returns the number of nodes contained in the grapher.
*/
public int getNumberOfNodes() {
return numberOfNodes;
}
/**
* Returns the number of links contained in the grapher.
*/
public int getNumberOfLinks() {
return numberOfLinks;
}
/**
* Returns the number of subgraphs contained in the grapher.
*/
public int getNumberOfSubgraphs() {
return numberOfSubgraphs;
}
/**
* Returns <code>true</code> if the GUI is enabled.
*/
public boolean isGUIEnabled() {
return isGUIenabled;
}
/**
* Returns the standard color used for nodes.
*/
public Color getStandardColor() {
return standardColor;
}
// --------------------------------------------------------------------------
// Initialization Routines / Creation if the GUI
/**
* Create the main window.
*/
private void createMainWindow() {
// open the frame, if appropriate
mainFrame = new JFrame("JViews Diagrammer - Graph Layout Gallery");
mainFrame.setSize(MAIN_WINDOW_DEFAULT_WIDTH, MAIN_WINDOW_DEFAULT_HEIGHT);
Point p = getMainWindowLocation();
mainFrame.setLocation(p.x, p.y);
// create the grapher and its auxiliaries
grapher = new IlvGrapher();
mgranim = new IlvManagerAnimator(grapher);
// create the GUI with buttons etc. including the scroll manager view
mainContainer = mainFrame != null ? mainFrame.getContentPane() : new JPanel();
mainContainer.setFont(new Font("sansserif", Font.PLAIN, 12));
createGUI(mainContainer);
// create the overview handler
overviewHandler = new OverviewHandler(this, mgrview, overviewButton,
getOverviewWindowLocation(), getOverviewWindowSize());
// create the help handler
helpHandler = new HelpHandler(this, getImage("gif/Background.gif"), helpButton,
getHelpWindowLocation(), getHelpWindowSize()) {
Override
public URL convertHyperlinkURL(URL inputURL) {
if (inputURL.toString().endsWith("#Description")) {
int layoutIndex = layoutChooser.getSelectedIndex();
int sizeIndex = sizeChooser.getSelectedIndex();
String layoutFileNamePrefix = layoutDescriptions[layoutIndex].getLayoutFileNamePrefix();
String sizeName = sizeChooser.getItemAt(sizeIndex);
return getClass().getResource(
"htmlgif/" + getGraphFileBaseName(layoutFileNamePrefix, sizeName) + ".html");
}
return inputURL;
}
};
// attach the grapher to the layout instances
// (only needed for flat graphs)
IlvGrapherAdapter adapter = flatGraphDemoSupport.getGrapherAdapter();
IlvGraphLayout layout = null;
for (int i = 0; i < layoutDescriptions.length; i++) {
layout = layoutDescriptions[i].getLayout();
if (layout != null)
layout.attach(adapter);
}
// finish the frame and open it
if (mainFrame != null) {
mainFrame.setIconImage(iconifizedIcon);
mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
mainFrame.addWindowListener(new WindowAdapter() {
Override
public void windowClosing(WindowEvent e) {
exit();
}
});
// frame.setVisible(true);
}
}
/**
* Setup the GUI. The GUI consists of 3 parts: - at the top panel, the graph
* selector and the interactor buttons, - at the bottom, a small text area for
* messages - in the middle, the scroll manager view
*/
private void createGUI(Container mainContainer) {
// create the expand cursor
Dimension dd = Toolkit.getDefaultToolkit().getBestCursorSize(16, 16);
Image cursorImage1, cursorImage2;
if (dd.width > 16 || dd.height > 16) {
cursorImage1 = getImage("gif/ExpandIdleCursor32x32.gif");
cursorImage2 = getImage("gif/ExpandCursor32x32.gif");
} else {
cursorImage1 = getImage("gif/ExpandIdleCursor16x16.gif");
cursorImage2 = getImage("gif/ExpandCursor16x16.gif");
}
expandIdleCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage1, new Point(3, 3),
"ExpandIdleCursor");
expandWorkCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage2, new Point(3, 3),
"ExpandWorkCursor");
// get the dimension
Dimension d = mainContainer.getSize();
// set the layout manager
mainContainer.setLayout(new BorderLayout(0, 0));
// first, the stuff in the middle:
mgrview = new ManagerView(grapher) {
Override
public void afterVerifyTransformer() {
if (!isAutoPlay()) {
SwingUtilities.invokeLater(new Runnable() {
Override
public void run() {
if (isBusy())
return;
if (mgrview.isMaximumZoom()) {
if (mgrview.getInteractor() == zoomViewInteractor)
setInteractionMode(SELECT_MODE);
}
}
});
checkZoomButtons();
}
}
};
prepareManagerView(mgrview);
scrollManView = new IlvJScrollManagerView(mgrview);
scrollManView.setWheelScrollingEnabled(true);
scrollManView.setBounds(0, d.height / 7, d.width, d.height * 5 / 7);
mainContainer.add("Center", scrollManView);
// now the panel at the top of the window, for the graph selector and the
// buttons.
topPanel = createTopPanel();
mainContainer.add("North", topPanel);
layoutButton.setSize(44, 30);
layoutButton.setPreferredSize(new Dimension(44, 30));
// finally the stuff for the bottom panel:
textArea = new ScrollHTMLTextArea(this, TEXT_AREA_HEIGHT);
bottomPanel = new JPanel() {
Override
public Insets getInsets() {
return new Insets(5, 5, 5, 5);
}
};
bottomPanel.setLayout(new BorderLayout(5, 5));
bottomPanel.add("Center", textArea);
mainContainer.add("South", bottomPanel);
// Must be called one grapher, mgrview etc. are already instanciated.
flatGraphDemoSupport = new FlatGraphLayoutSupport(this);
nestedGraphDemoSupport = new NestedGraphLayoutSupport(this);
}
/**
* Creates the top panel.
*/
private final JPanel createTopPanel() {
JPanel panel = new JPanel();
panel.setBackground(GUI_BACKGROUND_COLOR);
panel.setLayout(new BorderLayout(0, 0));
JPanel panelA = new JPanel();
panel.add("North", panelA);
panelA.setBackground(GUI_BACKGROUND_COLOR);
panelA.setLayout(new BorderLayout());
JPanel panelB = new JPanel();
panel.add("Center", panelB);
panelB.setBackground(GUI_BACKGROUND_COLOR);
panelB.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
// the panelA consists of two subpanel
JPanel auxPanel1 = new JPanel();
panelA.add("West", auxPanel1);
auxPanel1.setBackground(GUI_BACKGROUND_COLOR);
auxPanel1.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
JPanel auxPanel2 = new JPanel();
panelA.add("East", auxPanel2);
auxPanel2.setBackground(GUI_BACKGROUND_COLOR);
auxPanel2.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
// create the menu for selecting the layout algorithm
auxPanel1.add(new JLabel("Style:"));
layoutChooser = new JComboBox<String>();
layoutChooser.setToolTipText("Select the layout algorithm");
// item listener for layoutChooser and sizeChoser
ItemListener sampleSelection = new ItemListener() {
Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.DESELECTED)
return;
readGraph();
if (isAutomaticLayoutRun())
arrange(currentLayout, grapher, false);
else
appendMessage("<br>Please press the layout button " + "to perform a layout.", true);
}
};
for (int i = 0; i < layoutDescriptions.length; i++)
layoutChooser.addItem(layoutDescriptions[i].getLayoutName());
auxPanel1.add(layoutChooser);
layoutChooser.addItemListener(sampleSelection);
// create the menu for the size of the graph and for the layout style
auxPanel2.add(new JLabel(" Graph Size:"));
sizeChooser = new JComboBox<String>();
sizeChooser.setToolTipText("Select the size of the sample graph");
sizeChooser.addItem("small");
sizeChooser.addItem("medium");
sizeChooser.addItem("large");
auxPanel2.add(sizeChooser);
sizeChooser.addItemListener(sampleSelection);
// add all the buttons
addButtons(panelB);
if (WITH_LOGO) {
ImageIcon logoIcon = new ImageIcon(getImage("gif/IlogLogoTransparent.gif"));
JLabel logoLabel = new JLabel(logoIcon);
JLabel webLabel = new JLabel("http://www.roguewave.com/");
webLabel.setForeground(new Color(29, 0, 250));
Font font = webLabel.getFont();
webLabel.setFont(new Font(font.getFontName(), font.getStyle(), 18));
JPanel panelLogos = new JPanel();
panelLogos.setBackground(GUI_BACKGROUND_COLOR);
panelLogos.add(logoLabel);
panelLogos.add(webLabel);
panel.add("East", panelLogos);
}
return panel;
}
/**
* Adds the interactor buttons (zoom, unzoom etc.)
*/
private void addButtons(JPanel panel1) {
layoutButton = addButton(panel1, "Layout", new LayoutAction(), "Perform layout", false, false);
// the layout button is a little bit wider
layoutButton.setSize(44, 22);
layoutButton.setPreferredSize(new Dimension(44, 22));
randomizeButton =
addButton(panel1, "Random", new RandomizeAction(), "Randomize the positions", false, false);
showAllButton =
addButton(panel1, "ShowAll", new FitAction(), "Display the entire graph", false, false);
identityButton = addButton(panel1, "ZoomIdentity", new IdentityAction(),
"Original size (scale 1:1)", false, false);
zoomButton = addButton(panel1, "ZoomIn", new ZoomAction(), "Zoom in", false, false);
unzoomButton = addButton(panel1, "ZoomOut", new UnZoomAction(), "Zoom out", false, false);
zoomboxButton = addButton(panel1, "ZoomBox", new ZoomBoxPermanentAction(),
"Put the graph into Zoom Mode. Drag a rectangle " + "to zoom into that area", true, false);
selectButton = addButton(panel1, "Select", new SelectAction(), "Select objects", true, true);
expandButton = addButton(panel1, "Expand", new ExpandAction(), "Expand or collapse subgraphs",
true, false);
// the action listener of the overview button is installed by the handler
overviewButton =
addButton(panel1, "Overview", null, "Show or hide the Overview Window", true, false);
animationButton = addButton(panel1, "Animation", new AnimationAction(),
"Enable or disable animation", true, true);
// the action listener of the help button is installed by the handler
helpButton = addButton(panel1, "Help", null, "How to use this demo", true, false);
// only one of select/expand/zoombox interactors can be active
ButtonGroup group = new ButtonGroup();
group.add(selectButton);
group.add(expandButton);
group.add(zoomboxButton);
}
/**
* Adds a button containing an image.
*
* @return The button
*/
private AbstractButton addButton(JPanel panel, String iconName, ActionListener actionListener,
String helpMessage, boolean toggle, boolean selected) {
return addButton(PATH_TO_LOCAL_BUTTON_IMAGES, panel, iconName + ".gif", iconName + "Sel.gif",
iconName + "Gray.gif", actionListener, helpMessage, toggle, selected);
}
// --------------------------------------------------------------------------
// Window locations
/**
* Returns the location of the main window when opening.
*/
private Point getMainWindowLocation() {
Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
int x, y;
int hwR = HELP_WINDOW_DEFAULT_WIDTH + 15;
int hwB = HELP_WINDOW_DEFAULT_HEIGHT + 3;
if (screenDim.width >= hwR + MAIN_WINDOW_DEFAULT_WIDTH) {
x = hwR;
y = 3;
} else {
x = screenDim.width - MAIN_WINDOW_DEFAULT_WIDTH;
if (screenDim.height >= hwB + MAIN_WINDOW_DEFAULT_HEIGHT) {
x = 3;
y = hwB;
} else
y = screenDim.height - MAIN_WINDOW_DEFAULT_HEIGHT;
}
if (x < 3)
x = 3;
if (y < 3)
y = 3;
return new Point(x, y);
}
/**
* Returns the location of the help window when opening.
*/
private Point getHelpWindowLocation() {
return new Point(3, 3);
}
/**
* Returns the size of the help window when opening.
*/
private Dimension getHelpWindowSize() {
return new Dimension(HELP_WINDOW_DEFAULT_WIDTH, HELP_WINDOW_DEFAULT_HEIGHT);
}
/**
* Returns the location of the overview window when opening.
*/
private Point getOverviewWindowLocation() {
Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
Dimension d = getOverviewWindowSize();
Point mwl = getMainWindowLocation();
int x, y;
int hwB = HELP_WINDOW_DEFAULT_HEIGHT + 31;
if (mwl.x <= 3) {
x = mwl.x + HELP_WINDOW_DEFAULT_WIDTH;
if (x + d.width > screenDim.width)
x = screenDim.width - d.width;
y = 3;
} else {
if (screenDim.height >= hwB + d.height)
y = hwB;
else
y = screenDim.height - d.height;
x = mwl.x - d.width - 6;
}
if (x < 3)
x = 3;
if (y < 3)
y = 3;
return new Point(x, y);
}
/**
* Returns the size of the overview window when opening.
*/
private Dimension getOverviewWindowSize() {
Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
int w = screenDim.width / 4;
int h = screenDim.height / 4;
if (w < OVERVIEW_MIN_SIZE)
w = OVERVIEW_MIN_SIZE;
if (w > OVERVIEW_MAX_SIZE)
w = OVERVIEW_MAX_SIZE;
if (h < OVERVIEW_MIN_SIZE)
h = OVERVIEW_MIN_SIZE;
if (h > OVERVIEW_MAX_SIZE)
h = OVERVIEW_MAX_SIZE;
return new Dimension(w, h);
}
// --------------------------------------------------------------------------
// The auto play mechanism
/**
* Plays automatically an endless loop for loading successively the sample
* graphs and running the corresponding layout.
*/
private void autoPlay() {
int nLayouts = layoutChooser.getItemCount();
int nSizes = sizeChooser.getItemCount();
long delay = getAutoPlayDelay();
enableGUI(false);
sleep(500);
while (true) {
for (int i = 0; i < nLayouts; i++) {
for (int j = 0; j < nSizes; j++) {
grapher.setContentsAdjusting(true);
scrollManView.setAdjusting(true);
try {
readGraph(i, j);
if (layoutAllowsNodeShapes()) {
setNodeShapeOption(SPHERE_NODE_SHAPE);
} else {
setNoNodeShapeChoice(true);
}
arrange(currentLayout, grapher, false);
// at this moment, the demo is busy (the layout and the animation
// are running). We wait until it's ready before passing to the
// next sample.
try {
mgranim.getAnimationThread().join();
} catch (InterruptedException ex) {
}
// one early sleep to have time to redraw before the garbage
// collection
sleep(delay > 100 ? delay / 2 : 100);
} finally {
// break reference to the internal thread of the animator
mgranim.detach();
mgranim.attach(grapher);
grapher.setContentsAdjusting(false);
scrollManView.setAdjusting(false);
scrollManView.adjustScrollBars();
// now that it's ready, sleep the specified time to let the user
// admire the result (we also use this opportunity to do same gc...)
System.gc();
System.gc();
System.gc();
// we do the sleep at last to minimize the possibility that a
// pending invokeLater is unsynchronized done (which would be a
// bug anyway) before we continue
sleep(delay > 100 ? delay / 2 : 100);
}
}
}
}
}
// --------------------------------------------------------------------------
// Reading a graph from file
/**
* Make everything visible or invisible.
*/
private void setGrapherVisible(boolean flag) {
if (grapher != null && mgrview != null) {
for (int i = 0; i < grapher.getLayersCount(); i++)
mgrview.setVisible(i, flag);
}
}
/**
* Reads the currently selected graph from an ivl file.
*/
private void readGraph() {
setDefaultNodeShape();
readGraph(layoutChooser.getSelectedIndex(), sizeChooser.getSelectedIndex());
}
/**
* Reads a the sample graph for a given layout and with a given size.
*/
private void readGraph(int layoutIndex, int sizeIndex) {
// select the layout and get the name of the selected layout
String layoutName = layoutChooser.getItemAt(layoutIndex);
String layoutFileNamePrefix = null;
for (int i = 0; i < layoutDescriptions.length; i++) {
if (layoutName.equals(layoutDescriptions[i].getLayoutName())) {
layoutFileNamePrefix = layoutDescriptions[i].getLayoutFileNamePrefix();
currentLayout = layoutDescriptions[i].getLayout();
break;
}
}
if (layoutFileNamePrefix == null)
throw new IllegalArgumentException("layout file name prefix not found: " + layoutName);
String sizeName = sizeChooser.getItemAt(sizeIndex);
String graphFileBaseName = getGraphFileBaseName(layoutFileNamePrefix, sizeName);
currentGraphFileBaseName = graphFileBaseName;
// the name of the file that will be read from the jar file
String graphFileName = "ivl/" + graphFileBaseName + ".ivl";
readGraph(layoutName, layoutFileNamePrefix, sizeName, graphFileName);
}
/**
* Returns the name of the IVL file storing the graph, without any directory
* prepended and without any extension.
*/
private String getGraphFileBaseName(String layoutFileNamePrefix, String sizeName) {
return layoutFileNamePrefix + "-" + sizeName;
}
/**
* Reads a given file.
*/
private void readGraph(String layoutName, String shortLayoutName, String sizeName,
String graphFileName) {
// clear the text area
showMessage("");
// just to be sure, disable autolayout
linkLayout.setAutoLayout(false);
// reset the information of the node shape
currentNodeShape = SPHERE_NODE_SHAPE;
// save the current cursor
Cursor oldCursor = getDemoCursor();
// set the wait cursor
setDemoCursor(WAIT_CURSOR);
try {
// show help
if (firstTimeRead)
helpHandler.setHelpFile("help");
else
helpHandler.setHelpFile(getGraphFileBaseName(shortLayoutName, sizeName));
String sz = sizeName;
sz = sz.substring(0, 1).toUpperCase() + sz.substring(1);
showMessage(getBlueBoldMessage("The " + sz + " " + layoutName + " Example") + "<br>");
// remove all nodes and links
grapher.deleteAll(false);
appendMessage("Reading " + graphFileName + " ... ");
// read the files from the jar
setGrapherVisible(false);
grapher.read(getClass().getResourceAsStream(graphFileName));
// If you need to dump the file in to ASCI IVL
// grapher.write(getGraphFileBaseName(shortLayoutName, sizeName) +
// "-ascii.ivl", false);
// calculate the size information
calcSize(grapher);
// do it once calcSize has been called!
layoutSupport = getNumberOfSubgraphs() == 0 ? flatGraphDemoSupport : nestedGraphDemoSupport;
// enable this in order to make graphs smaller
layoutSupport.prepareSample(graphFileName);
setBackgroundStyle(0);
// enable this in order to make graphs smaller
// writeGraph(getGraphFileBaseName(shortLayoutName, sizeName));
// preparation after read
afterRead();
// and show the size information
appendMessage("" + numberOfNodes + " nodes, " + numberOfLinks + " links, " + numberOfSubgraphs
+ " subgraphs. ");
} catch (Exception e) {
System.err.println("error during read: " + graphFileName);
e.printStackTrace();
showMessage("error during read: " + graphFileName);
} finally {
// pop the frame to front
if (mainFrame != null)
mainFrame.toFront();
// restore the original cursor
setDemoCursor(oldCursor);
}
}
/**
* Is called after reading the graph from file.
*/
private void afterRead() {
// enable/disable the expand button
SwingUtilities.invokeLater(new Runnable() {
Override
public void run() {
setInteractionMode(SELECT_MODE);
if (expandButton != null)
expandButton.setEnabled(expandAllowed() && !isAutoPlay());
}
});
// a new standard color for this graph
Color c = imageUtil.getRandomColor();
while (c == standardColor)
c = imageUtil.getRandomColor();
standardColor = c;
if (currentLayout instanceof IlvTopologicalMeshLayout) {
// to avoid that the layout remembers the old starting node
((IlvTopologicalMeshLayout) currentLayout).setStartingNode(null);
}
// whether we have the choice of a node shape. This must be determined
// before preparing the grapher
boolean allowsNodeShapes = layoutAllowsNodeShapes();
setNoNodeShapeChoice(!allowsNodeShapes);
if (allowsNodeShapes)
convertColors();
// transfer all layout parameters from the names properties
loadLayoutProperties();
// finally, remove the layout properties to cleanup the memory
cleanLayoutProperties();
// prepare the grapher
prepareGrapherAfterRead();
// now analize the sample type and decide what to do
// whether the link clip provider is needed
if (currentLayout != null && currentLayout.supportsLinkClipping()
&& !(currentLayout instanceof IlvHierarchicalLayout))
currentLayout.setLinkClipInterface(linkClipProvider);
// whether autolayout is enabled
if (currentLayout instanceof IlvLinkLayout) {
linkLayout.setAutoLayout(true);
selectionInteractor.setOpaqueMove(false);
} else {
linkLayout.setAutoLayout(false);
selectionInteractor.setOpaqueMove(true);
}
if (currentLayout instanceof IlvUniformLengthEdgesLayout) {
// Because the nodes can have different sizes, ask the
// Uniform Length Edges
// layout to take into account the size of each node.
// This parameter can also be saved with the IVL file.
((IlvUniformLengthEdgesLayout) currentLayout).setRespectNodeSizes(true);
((IlvUniformLengthEdgesLayout) currentLayout).setPreferredLinksLength(50);
}
if (currentLayout instanceof IlvTreeLayout) {
((IlvTreeLayout) currentLayout).setConnectorStyle(IlvTreeLayout.CLIPPED_PINS);
}
// whether layout can use randomization
setRandomizeAllowed(layoutAllowsRandomization());
// whether layout can use animation
setAnimationAllowed(layoutAllowsAnimation());
// which layout samples allow fit to view after layout
if (!layoutAllowsFitToView() || !isAutomaticLayoutRun()) {
// fit after read, because we cannot fit after layout
setGrapherVisible(true);
fit(mgrview, true);
} else {
mgrview.setTransformer(new IlvTransformer());
setGrapherVisible(true);
grapher.reDraw();
}
}
/**
* Loads the layout properties after reading from file.
*/
public void loadLayoutProperties() {
layoutSupport.loadLayoutProperties();
}
/**
* Cleans up the layout properties after reading or writing to/from file.
*/
public void cleanLayoutProperties() {
layoutSupport.cleanLayoutProperties();
}
/**
* Make all labels selectable or nonselectable.
*/
public void makeLabelsSelectable(boolean flag) {
layoutSupport.makeLabelsSelectable(flag);
// and mark all labels as nonselectable
IlvGraphicEnumeration objects = grapher.getObjects(true);
while (objects.hasMoreElements()) {
IlvGraphic g = objects.nextElement();
if (g instanceof IlvZoomableLabel)
((IlvManager) g.getGraphicBag()).setSelectable(g, flag);
}
}
/**
* Prepares the grapher after reading.
*/
private void prepareGrapherAfterRead() {
if (getNumberOfSubgraphs() == 0)
prepareFlatGrapherAfterRead();
layoutSupport.prepareGrapherAfterRead();
}
/**
* Prepares the flat grapher after reading.
*/
public void prepareFlatGrapherAfterRead() {
IlvGrapher grapher = getGrapher();
// change the node shape, if necessary
changeNodeShape(grapher);
// install Links
IlvGraphicEnumeration objs = grapher.getObjects();
IlvGraphic g;
Vector<IlvLinkImage> links = new Vector<IlvLinkImage>();
while (objs.hasMoreElements()) {
g = objs.nextElement();
if (grapher.isLink(g))
links.addElement((IlvLinkImage) g);
}
grapher.setContentsAdjusting(true);
try {
Enumeration<IlvLinkImage> e = links.elements();
IlvLinkImage oldlink;
IlvLinkImage newlink;
while (e.hasMoreElements()) {
oldlink = e.nextElement();
if (oldlink instanceof IlvSplineLinkImage) {
newlink = new SplineLink(oldlink.getFrom(), oldlink.getTo(), oldlink.isOriented(), null);
((SplineLink) newlink).setDefinitionPoints(oldlink.getLinkPoints(null));
} else {
newlink = new Link(oldlink.getFrom(), oldlink.getTo(), oldlink.isOriented(), null);
((Link) newlink).setDefinitionPoints(oldlink.getLinkPoints(null));
}
replaceLink(grapher, oldlink, newlink);
}
} finally {
grapher.setContentsAdjusting(false);
}
// install clip link connectors
grapher.setContentsAdjusting(true);
IlvLinkConnector lc;
try {
objs = grapher.getObjects();
while (objs.hasMoreElements()) {
g = objs.nextElement();
// make all nodes and links non editable
grapher.setEditable(g, false);
if (grapher.isNode(g)) {
lc = IlvClippingLinkConnector.GetAttached(g);
if (lc != null)
lc.detach(false);
new IlvClippingLinkConnector(g);
}
}
} finally {
grapher.setContentsAdjusting(false);
}
}
// --------------------------------------------------------------------------
// Writing a graph from file
// This is normally not used. However, in order to convert the samples
// into minimized binary form, we use it when preparing the web demo.
// What we do when preparing the demo app is the following:
// We enable writeGraph, and run the demo in autoPlay mode.
// Then we compare the size of the written graphs with the size of the
// original graphs, and choose the smaller of both. Normally, no information
// is lost that cannot be recalculated inside the app, so it is safe.
/**
* Write the graph to file.
*/
/*
* private void writeGraph(String fileName) { // load layout parameters
* adapter.loadParametersFromNamedProperties(currentLayout); // to get a clean situation
* adapter.removeParametersFromNamedProperties();
*
* // the following are various mechanism to make the size of ivl files // smaller
*
* IlvGraphicVector nodeVector = new IlvGraphicVector(1000); IlvGraphicVector linkVector = new
* IlvGraphicVector(1000);
*
*
* // put the nodes and links in vectors IlvGraphicEnumeration allObjects = grapher.getObjects();
* IlvGraphic obj; while (allObjects.hasMoreElements()) { obj = allObjects.nextElement(); if
* (grapher.isNode(obj)) nodeVector.addElement(obj); if (grapher.isLink(obj))
* linkVector.addElement(obj); }
*
* // now replace all relief labels by normal labels to save space if (layoutAllowsNodeShapes()) {
* grapher.setContentsAdjusting(true); try { int nNodes = nodeVector.size(); IlvGraphic node;
*
* for (int i = 0; i < nNodes; i++) { node = nodeVector.elementAt(i); if (node instanceof
* IlvReliefLabel) { IlvReliefLabel oldNode = (IlvReliefLabel)node; if (oldNode.getLabel() == null
* || oldNode.getLabel().equals("")) { IlvRect rect = oldNode.boundingBox(null); IlvRectangle
* newNode = new IlvRectangle(rect); replaceNode(grapher, oldNode, newNode);
* newNode.setForeground(oldNode.getBackground()); grapher.reshapeObject(newNode, rect, false); }
* } else if (node instanceof IlvReliefRectangle) { IlvReliefRectangle oldNode =
* (IlvReliefRectangle)node; IlvRect rect = oldNode.boundingBox(null); IlvRectangle newNode = new
* IlvRectangle(rect); replaceNode(grapher, oldNode, newNode);
* newNode.setForeground(oldNode.getBackground()); grapher.reshapeObject(newNode, rect, false); }
* } } finally { grapher.setContentsAdjusting(false); } }
*
* // replace polyline links without intermediate points by linkimages.
* grapher.setContentsAdjusting(true); try { int nLinks = linkVector.size(); IlvGraphic link;
*
* for (int i = 0; i < nLinks; i++) { link = linkVector.elementAt(i); if (link instanceof
* IlvPolylineLinkImage) { IlvPolylineLinkImage oldlink = (IlvPolylineLinkImage)link; if
* (oldlink.getPointsCardinal() == 2) { IlvLinkImage newlink = new IlvLinkImage(oldlink.getFrom(),
* oldlink.getTo(), oldlink.isOriented()); replaceLink(grapher, oldlink, newlink); } } } } finally
* { grapher.setContentsAdjusting(false); }
*
* // remove all link connectors (because we reinstall them on loading anyway)
* IlvGraphicEnumeration objs = grapher.getObjects(); IlvGraphic g; Vector links = new Vector();
* while (objs.hasMoreElements()) { g = objs.nextElement(); if (grapher.isNode(g)) {
* IlvLinkConnector lc = IlvLinkConnector.Get(g); if (lc != null) lc.detach(false); } }
*
* if (currentLayout.supportsSaveParametersToNamedProperties())
* adapter.saveParametersToNamedProperties(currentLayout, false);
*
* try { grapher.write(fileName, true); } catch (IOException ex) { ex.printStackTrace(); } }
*/
// --------------------------------------------------------------------------
// Small auxiliaries after reading
/**
* Converts the colors to the standard colors.
*/
private void convertColors() {
layoutSupport.convertColors();
}
/**
* Calculates the size of the graph after reading.
*/
private void calcSize(IlvGrapher grapher) {
numberOfNodes = 0;
numberOfLinks = 0;
numberOfSubgraphs = 0;
calcSizeRecursive(grapher);
}
private void calcSizeRecursive(IlvGrapher grapher) {
// count the nodes and links
IlvGraphicEnumeration e = grapher.getObjects();
while (e.hasMoreElements()) {
IlvGraphic obj = e.nextElement();
if (grapher.isNode(obj)) {
if (obj instanceof IlvGrapher) {
numberOfSubgraphs++;
calcSizeRecursive((IlvGrapher) obj);
} else
numberOfNodes++;
} else if (grapher.isLink(obj))
numberOfLinks++;
}
}
// --------------------------------------------------------------------------
// Node shape management
/**
* Sets whether we have the choice of a node shape.
*/
private void setNoNodeShapeChoice(boolean flag) {
if (flag != noNodeShapeChoice) {
noNodeShapeChoice = flag;
}
}
/**
* Returns true if we have the choice of a node shape.
*/
private boolean isNoNodeShapeChoice() {
return noNodeShapeChoice;
}
/**
* Sets the node shape option.
*/
private void setNodeShapeOption(int shape) {
if (isBusy())
return;
if (shape == SPHERE_NODE_SHAPE || shape == RECTANGLE_NODE_SHAPE)
nodeShape = shape;
else
throw new IllegalArgumentException("unsupported node shape: " + shape);
}
/**
* Returns the node shape option.
*/
public int getNodeShapeOption() {
if (isNoNodeShapeChoice())
return RECTANGLE_NODE_SHAPE;
return nodeShape;
}
/**
* Set the default node shape of a graph depending on the size of the graph,
* just to avoid that the demo gets boring.
*/
private void setDefaultNodeShape() {
setNodeShapeOption(SPHERE_NODE_SHAPE);
}
/**
* Changes the shape of the nodes.
*/
public void changeNodeShape(IlvGrapher grapher) {
if (isBusy())
return;
setBackgroundStyle(getNodeShapeOption());
// if we have no choice, don't do anything
if (isNoNodeShapeChoice())
return;
// allocate vectors for the node shape replacement
IlvGraphicVector vectNodes = new IlvGraphicVector(1000);
// put the nodes in the Vector
IlvGraphicEnumeration allObjects = grapher.getObjects();
IlvGraphic obj;
while (allObjects.hasMoreElements()) {
obj = allObjects.nextElement();
if (grapher.isNode(obj))
vectNodes.addElement(obj);
}
// now replace all nodes
grapher.setContentsAdjusting(true);
try {
int nNodes = vectNodes.size();
IlvGraphic newNode;
IlvGraphic oldNode;
// save it for future use
currentNodeShape = getNodeShapeOption();
for (int i = 0; i < nNodes; i++) {
oldNode = vectNodes.elementAt(i);
newNode = createNode(oldNode, currentNodeShape);
replaceNode(grapher, oldNode, newNode);
}
} finally {
grapher.setContentsAdjusting(false);
}
grapher.reDraw();
}
/**
* Replace a link in the grapher.
*/
public void replaceLink(IlvGrapher grapher, IlvLinkImage oldLink, IlvLinkImage newLink) {
layoutSupport.replaceLink(grapher, oldLink, newLink);
}
/**
* Replace a node in the grapher.
*/
public void replaceNode(final IlvGrapher grapher, final IlvGraphic oldNode,
final IlvGraphic newNode) {
layoutSupport.replaceNode(grapher, oldNode, newNode);
}
/**
* Creates a node of the given shape.
*/
private IlvGraphic createNode(IlvGraphic oldNode, int shapeType) {
Color color = null;
if (layoutPreservesNodeColor())
color = (Color) oldNode.getProperty(BACKGROUND_COLOR);
switch (shapeType) {
case SPHERE_NODE_SHAPE:
return createSphereNode(color);
case RECTANGLE_NODE_SHAPE:
return createRectNode(color);
}
return oldNode;
}
/**
* Create a rectangle icon.
*/
private IlvGraphic createRectNode(Color color) {
IlvGraphic newNode = new IlvReliefRectangle(standardNodeRect);
if (color == null)
newNode.setBackground(standardColor);
else
newNode.setBackground(color);
return newNode;
}
/**
* Create a sphere node.
*/
private IlvGraphic createSphereNode(Color color) {
Ellipse node = new Ellipse(standardSphereNodeRect);
if (color == null)
color = standardColor;
color = imageUtil.getClosestGradColor(color);
float[] stops = new float[] {0f, 0.1f, 0.2f, 0.4f, 1f};
Color[] colors = new Color[5];
colors[0] = Color.white;
colors[1] = imageUtil.transformColor(255, 255, 185, color);
colors[2] = imageUtil.transformColor(255, 255, 0, color);
colors[3] = imageUtil.transformColor(217, 217, 0, color);
colors[4] = Color.black;
Paint paint = new IlvRadialGradientPaint(new Point2D.Double(0.52, 0.49), 0.66, stops, colors,
new Point2D.Double(0.34, 0.29), IlvMultipleGradientPaint.SPREAD_PAD,
IlvMultipleGradientPaint.LINEAR_RGB, null, true);
node.setPaint(paint);
node.setForeground(colors[3]);
return node;
}
// --------------------------------------------------------------------------
// Related to the background
/**
* Set the background style of the manager view.
*/
private void setBackgroundStyle(int style) {
mgrview.setBackgroundPatternLocation(getClass().getResource("gif/BackgroundBlack.gif"));
}
// --------------------------------------------------------------------------
// Accessor and set methods of the flags that control the app
/**
* Sets whether randomization is allowed.
*/
private void setRandomizeAllowed(boolean flag) {
if (randomizeAllowed != flag) {
if (!isAutoPlay())
randomizeButton.setEnabled(flag);
randomizeAllowed = flag;
}
}
/**
* Returns true if randomization is allowed.
*/
public boolean isRandomizeAllowed() {
return randomizeAllowed;
}
/**
* Sets whether animation is allowed.
*/
private void setAnimationAllowed(boolean flag) {
if (animationAllowed != flag) {
animationButton.setEnabled(flag);
animationAllowed = flag;
}
}
/**
* Returns true if randomization is allowed.
*/
private boolean isAnimationAllowed() {
return animationAllowed;
}
/**
* Sets whether we use animation.
*/
private void setAnimation(boolean enable) {
if (isBusy())
return;
animation = enable;
}
/**
* Returns true if animation is active.
*/
boolean isAnimation() {
if (!isAnimationAllowed())
return false;
return animation;
}
/**
* Returns true if automatic layout after loading is active.
*/
private boolean isAutomaticLayoutRun() {
return automaticLayoutRun && animation;
}
/**
* Returns true if the graph allows expand/collapse.
*/
private boolean expandAllowed() {
return getNumberOfSubgraphs() > 0 && !(getCurrentGraphFileBaseName() != null
&& getCurrentGraphFileBaseName().equals("mixed-nesting-small"));
}
/**
* Sets the auto play mode.
*/
private void setAutoPlay(boolean flag) {
autoPlay = flag;
}
/**
* Returns true if auto play mode is active.
*/
public boolean isAutoPlay() {
return autoPlay;
}
/**
* Sets the delay for auto play mode.
*/
private void setAutoPlayDelay(long delay) {
autoPlayDelay = delay;
}
/**
* Returns the autoplay delay.
*/
private long getAutoPlayDelay() {
return autoPlayDelay;
}
// --------------------------------------------------------------------------
// Layout style control
/**
* Returns true if the layout style needs to preserve the node color for
* rectangles as read from file.
*/
public boolean layoutPreservesNodeColor() {
return true;
}
/**
* Returns true if the layout style allows node shapes.
*/
private boolean layoutAllowsNodeShapes() {
return currentLayout != null && !(currentLayout instanceof IlvHierarchicalLayout
|| currentLayout instanceof IlvGridLayout || currentLayout instanceof IlvLinkLayout);
}
/**
* Returns true if the layout style allows to randomize the graph.
*/
private boolean layoutAllowsRandomization() {
return !(currentLayout instanceof IlvLinkLayout);
}
/**
* Returns true if the layout style allows animation.
*/
private boolean layoutAllowsAnimation() {
return true;
}
/**
* Returns true if the layout style allows fit to view after layout.
*/
public boolean layoutAllowsFitToView() {
// fit to view in link layout makes the nodes appear like moving during
// animations. We don't want this.
return !(currentLayout instanceof IlvLinkLayout);
}
// --------------------------------------------------------------------------
// Related to the interactions
/**
* Returns the expand cursor used when over an object that is not a subgraph.
*/
public Cursor getExpandIdleCursor() {
return expandIdleCursor;
}
/**
* Returns the expand cursor used when over an object that is a subgraph.
*/
public Cursor getExpandWorkCursor() {
return expandWorkCursor;
}
/**
* Sets the interaction mode.
*/
public void setInteractionMode(int mode) {
if (isBusy())
return;
switch (mode) {
case SELECT_MODE:
if (selectButton != null && !selectButton.isSelected())
selectButton.setSelected(true);
if (expandButton != null)
expandButton.setSelected(false);
if (zoomboxButton != null)
zoomboxButton.setSelected(false);
if (mgrview.getInteractor() != selectionInteractor) {
mgrview.popInteractor();
mgrview.pushInteractor(selectionInteractor);
appendMessage("<br>Click to select objects!");
}
break;
case EXPAND_MODE:
if (expandButton != null && !expandButton.isSelected())
expandButton.setSelected(true);
if (selectButton != null)
selectButton.setSelected(false);
if (zoomboxButton != null)
zoomboxButton.setSelected(false);
if (expandInteractor == null)
expandInteractor = new ExpandInteractor(this);
if (mgrview.getInteractor() != expandInteractor) {
mgrview.popInteractor();
mgrview.pushInteractor(expandInteractor);
appendMessage("<br>Click on subgraphs to expand or collapse!");
}
break;
case ZOOMBOX_MODE:
if (zoomboxButton != null && !zoomboxButton.isSelected())
zoomboxButton.setSelected(true);
if (selectButton != null)
selectButton.setSelected(false);
if (expandButton != null)
expandButton.setSelected(false);
if (zoomViewInteractor == null) {
zoomViewInteractor = new IlvZoomViewInteractor();
zoomViewInteractor.setPermanent(true);
}
if (mgrview.getInteractor() != zoomViewInteractor) {
mgrview.popInteractor();
mgrview.pushInteractor(zoomViewInteractor);
appendMessage("<br>Drag a rectangle to zoom to this rectangle!");
}
break;
}
}
/**
* Arranges the graph by performing layout.
*/
private void arrange(IlvGraphLayout layout, IlvGrapher grapher, boolean isInitialLayout) {
if (!isBusy())
layoutSupport.arrange(layout, grapher, isInitialLayout);
}
/**
* Randomize the node positions.
*/
public void randomize() {
if (!isBusy())
layoutSupport.randomize();
}
// --------------------------------------------------------------------------
// The corresponding actions
public class AnimationAction implements ActionListener {
Override
public void actionPerformed(ActionEvent e) {
setAnimation(animationButton.isSelected());
}
}
public class LayoutAction implements ActionListener {
Override
public void actionPerformed(ActionEvent e) {
arrange(currentLayout, grapher, false);
}
}
public class RandomizeAction implements ActionListener {
Override
public void actionPerformed(ActionEvent e) {
randomize();
}
}
public class SelectAction implements ActionListener {
Override
public void actionPerformed(ActionEvent e) {
setInteractionMode(SELECT_MODE);
}
}
public class ExpandAction implements ActionListener {
Override
public void actionPerformed(ActionEvent e) {
setInteractionMode(EXPAND_MODE);
}
}
public class ZoomBoxPermanentAction implements ActionListener {
Override
public void actionPerformed(ActionEvent e) {
setInteractionMode(ZOOMBOX_MODE);
}
}
/**
* Enables or disables the interactor buttons.
*/
public void enableInteractorButtons(boolean enable) {
enableZoomButtons(enable);
if (layoutButton != null)
layoutButton.setEnabled(enable);
if (randomizeButton != null)
randomizeButton.setEnabled(enable && isRandomizeAllowed());
if (selectButton != null)
selectButton.setEnabled(enable);
if (expandButton != null)
expandButton.setEnabled(enable && expandAllowed());
if (overviewButton != null)
overviewButton.setEnabled(enable);
if (animationButton != null)
animationButton.setEnabled(enable && isAnimationAllowed());
if (helpButton != null)
helpButton.setEnabled(enable);
}
// --------------------------------------------------------------------------
// Performing layout
/**
* Preprocessing before layout.
*/
void beforeLayout(IlvGraphLayout layout) {
// to avoid that the animation gets too slow
if (numberOfNodes + numberOfLinks > 300) {
mgranim.setAnimationDelay(20);
mgranim.setAnimationRate(20);
} else {
mgranim.setAnimationDelay(50);
mgranim.setAnimationRate(20);
}
// make the progressbar aware of the current layout
// progressBar.setGraphLayout(layout);
// mark the app as busy to prevent further input
setBusy(true);
// disable the GUI
if (Thread.currentThread() == mgranim.getAnimationThread()) {
enableGUI(false);
while (isGUIenabled) {
sleep(300);
}
}
// disable autolayout
autoLayout = linkLayout.isAutoLayout();
linkLayout.setAutoLayout(false);
// specific preparations per layout style
if (layout instanceof IlvTopologicalMeshLayout)
prepareMeshLayout((IlvTopologicalMeshLayout) layout);
else if (layout instanceof IlvUniformLengthEdgesLayout)
prepareUniformLayout((IlvUniformLengthEdgesLayout) layout);
}
/**
* Prepare the layout paramters for mesh layout.
*/
private void prepareMeshLayout(IlvTopologicalMeshLayout layout) {
// increase the allowed time for big graphs
if (numberOfNodes > 40)
layout.setLayoutRegion(new IlvRect(0, 0, 700, 700));
else
layout.setLayoutRegion(new IlvRect(0, 0, 500, 500));
}
/**
* Prepare the layout paramters for uniform edges length layout.
*/
private void prepareUniformLayout(IlvUniformLengthEdgesLayout layout) {
// increase the allowed time for big graphs
if (numberOfNodes > 100)
layout.setAllowedTime(80000); // ms
else
layout.setAllowedTime(32000);
}
/**
* Postprocessing after layout.
*/
public void afterLayout(IlvGraphLayout layout) {
// mark the app as not busy anymore
setBusy(false);
// enable the GUI
if (!isAutoPlay()) {
enableGUI(true);
// restore auto layout
linkLayout.setAutoLayout(autoLayout);
}
}
/**
* Enable or disable the buttons of the GUI.
*/
public void enableGUI(boolean flag) {
final boolean enable = flag;
// avoid enable/disable for nothing.
SwingUtilities.invokeLater(new Runnable() {
Override
public void run() {
if (enable != isGUIenabled) {
if (sizeChooser != null)
sizeChooser.setEnabled(enable);
if (layoutChooser != null)
layoutChooser.setEnabled(enable);
enableInteractorButtons(enable);
isGUIenabled = enable;
if (mainContainer != null) {
if (enable) {
// restore the original cursor
setDemoCursor(oldCursor);
} else {
// save the current cursor
oldCursor = getDemoCursor();
// set the wait cursor
setDemoCursor(WAIT_CURSOR);
}
}
}
}
});
}
// --------------------------------------------------------------------------
// Others
Override
public void setDemoCursor(Cursor cursor) {
super.setDemoCursor(cursor);
// the interactor for expand/collapse sets the cursor
// directly on the manager view, so when changing
// the cursor globally we need to set it there explicitly
if (mgrview != null)
mgrview.setCursor(cursor);
}
/**
* Displays a message.
*/
Override
protected void showMessage(final String message) {
// well, it may happen that showMessage is called before the
// initialization of textArea...
if (textArea != null) {
if (SwingUtilities.isEventDispatchThread())
textArea.setMessage(message);
else {
SwingUtilities.invokeLater(new Runnable() {
Override
public void run() {
textArea.setMessage(message);
}
});
}
} else
System.out.println(message);
}
/**
* Appends a message.
*
* @param message
* the message
* @param showLastLine
* If true, the vertical scrollbar is scrolled such as the last added
* line gets visible
*/
Override
public void appendMessage(final String message, final boolean showLastLine) {
if (SwingUtilities.isEventDispatchThread())
textArea.appendMessage(message, showLastLine);
else {
SwingUtilities.invokeLater(new Runnable() {
Override
public void run() {
textArea.appendMessage(message, showLastLine);
}
});
}
}
/**
* Returns a text with blue and bold font.
*/
private String getBlueBoldMessage(String message) {
return "<b><font color=\"#000099\">" + message + "</font></b>";
}
// ----------------------------------------------------------------------------
// GUI Auxiliary Classes
// ----------------------------------------------------------------------------
/**
* A expand/collapse interactor.
*/
final class ExpandInteractor extends IlvSelectInteractor {
IlvLayoutGalleryApp app;
IlvApplyObject expandApplyObject;
Cursor oldCursor;
public ExpandInteractor(IlvLayoutGalleryApp app) {
this.app = app;
setDragAllowed(false);
setEditionAllowed(false);
setMoveAllowed(false);
setMultipleSelectionMode(true);
}
Override
protected void attach(IlvManagerView v) {
super.attach(v);
makeLabelsSelectable(true);
getManager().removeManagerSelectionListener(this);
getManager().addManagerTreeSelectionListener(this);
getManager().deSelectAll(true, true);
oldCursor = v.getCursor();
if (app.getExpandIdleCursor() != null)
v.setCursor(app.getExpandIdleCursor());
}
Override
protected void detach() {
getManager().removeManagerTreeSelectionListener(this);
makeLabelsSelectable(false);
if (oldCursor != null)
this.getManagerView().setCursor(oldCursor);
oldCursor = null;
super.detach();
}
private IlvGrapher getSubgrapher(IlvGraphic obj) {
while (obj != null && !(obj instanceof IlvGrapher))
obj = (IlvGraphic) obj.getGraphicBag();
// test whether it is the top level grapher
if (obj == null || obj.getGraphicBag() == null)
return null;
return (IlvGrapher) obj;
}
Override
protected void processMouseMotionEvent(MouseEvent event) {
switch (event.getID()) {
case MouseEvent.MOUSE_MOVED:
IlvPoint point = new IlvPoint(event.getX(), event.getY());
IlvManager topManager = getManager();
IlvManagerView view = this.getManagerView();
IlvGraphic obj = topManager.getObject(point, view, true);
IlvGrapher subgrapher = getSubgrapher(obj);
if (subgrapher != null && !app.isBusy()) {
if (app.getExpandWorkCursor() != null) {
if (view.getCursor() != app.getExpandWorkCursor())
view.setCursor(app.getExpandWorkCursor());
}
} else {
if (app.getExpandIdleCursor() != null) {
if (view.getCursor() != app.getExpandIdleCursor())
view.setCursor(app.getExpandIdleCursor());
}
}
break;
}
super.processMouseMotionEvent(event);
}
Override
public void selectionChanged(ManagerSelectionChangedEvent event) {
// get the selected or deselected object
IlvGraphic obj = event.getGraphic();
IlvManager manager = (IlvManager) obj.getGraphicBag();
if (manager == null)
return;
if (!manager.isSelected(obj))
return;
if (app.isBusy()) {
manager.setSelected(obj, false, false);
return;
}
if (expandApplyObject == null)
expandApplyObject = new IlvApplyObject() {
Override
public final void apply(IlvGraphic obj, Object arg) {
((IlvGrapher) obj).setCollapsed(!((IlvGrapher) obj).isCollapsed());
}
};
// get the subgrapher related to the object
IlvGrapher subgrapher = getSubgrapher(obj);
if (subgrapher != null) {
// this helps the tree layout
layoutSupport.makeIntergraphLinksStraightAt(subgrapher);
// collapse or expand.
subgrapher.getGraphicBag().applyToObject(subgrapher, expandApplyObject, null, true);
// incremental layout needed
layoutSupport.arrangeAfterExpand();
}
manager.setSelected(obj, false, false);
}
}
/**
* Stores info about a layout.
*/
private static class LayoutDescriptor {
private IlvGraphLayout _layout;
private String _layoutName;
private String _layoutFileNamePrefix;
LayoutDescriptor(String layoutName, String layoutFileNamePrefix) {
this(null, layoutName, layoutFileNamePrefix);
}
LayoutDescriptor(IlvGraphLayout layout, String layoutName, String layoutFileNamePrefix) {
_layout = layout;
_layoutName = layoutName;
_layoutFileNamePrefix = layoutFileNamePrefix;
}
IlvGraphLayout getLayout() {
return _layout;
}
String getLayoutName() {
return _layoutName;
}
String getLayoutFileNamePrefix() {
return _layoutFileNamePrefix;
}
}
}