/*
* 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.ComponentOrientation;
import java.awt.FlowLayout;
import java.awt.GridLayout;
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.text.MessageFormat;
import java.util.Locale;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import ilog.views.IlvDirection;
import ilog.views.IlvGrapher;
import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.IlvHandlesSelection;
import ilog.views.IlvManager;
import ilog.views.IlvManagerView;
import ilog.views.event.ManagerSelectionChangedEvent;
import ilog.views.event.ManagerSelectionListener;
import ilog.views.graphlayout.GraphLayoutEvent;
import ilog.views.graphlayout.GraphLayoutEventListener;
import ilog.views.graphlayout.IlvGraphLayout;
import ilog.views.graphlayout.IlvGraphLayoutException;
import ilog.views.graphlayout.IlvGraphLayoutReport;
import ilog.views.graphlayout.IlvGrapherAdapter;
import ilog.views.graphlayout.recursive.IlvRecursiveLayout;
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.swing.IlvJManagerViewControlBar;
import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.util.IlvLocaleUtil;
import ilog.views.util.IlvProductUtil;
import ilog.views.util.IlvResourceUtil;
import ilog.views.util.swing.IlvSwingUtil;
/**
* This is a very simple application that uses nested
* <code>IlvGrapher</code> to perform a recursive layout on a nested graph. It
* shows how to use recursive layout in applications that are not based on CSS
* styling.
*/
public class RecursiveLayoutApp extends JRootPane implements ManagerSelectionListener {
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 grapher adapter */
IlvGrapherAdapter adapter = new IlvGrapherAdapter(grapher);
/** The view of the grapher */
IlvManagerView mgrview = new IlvManagerView(grapher);
/** The recursive layout */
IlvRecursiveLayout recursiveLayout;
/** A graph layout event listener */
LayoutIterationListener layoutListener = new LayoutIterationListener();
/** GUI Parts */
JCheckBox sameLayoutEverywhereCheckBox = new JCheckBox(getString("sameLayoutCheckbox.Label"), true);
JComboBox<String> layoutModeSelector = new JComboBox<String>();
/** A text field to display messages */
JTextField msgLine = new JTextField();
/** the selected grapher during interactions */
IlvGraphic selectedObject = null;
/** constants for the layout mode */
static final int TREE = 0;
static final int RADIAL = 1;
static final int TIP_OVER = 2;
static final int UNIFORM = 3;
/** The layout mode */
int layoutMode = -1;
/** Whether we use reference layout mode or internal provider mode. */
boolean sameLayoutEverywhere = true;
/**
* Initializes the application.
*/
public void init() {
showMessage(getString("InitMessage"));
JPanel panel;
// set the layout mode and attach the layout
setLayoutMode(null, TREE);
// set the zoom range of the manager view
mgrview.setZoomFactorRange(0.05, 20);
// 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);
// Settings parameters for selection handles
IlvHandlesSelection.defaultHandleColor = Color.black;
IlvHandlesSelection.defaultHandleBackgroundColor = Color.white;
IlvHandlesSelection.defaultHandleShape = IlvHandlesSelection.SQUARE_SHAPE;
// set the layout manager
getContentPane().setLayout(new BorderLayout(0, 0));
// fit so far all together
getContentPane().add("Center", scrollManView);
getContentPane().add("North", panel = new JPanel());
panel.setLayout(new GridLayout(2, 2));
// create the standard control bar
IlvJManagerViewControlBar controlBar = new IlvJManagerViewControlBar();
controlBar.setView(mgrview);
// modify the interactors such that the demo looks better
IlvSelectInteractor selInter = (IlvSelectInteractor) controlBar.getSelectInteractor();
selInter.setOpaqueMove(true);
selInter.setOpaqueResize(true);
selInter.setOpaquePolyPointsEdition(true);
((IlvZoomViewInteractor) controlBar.getZoomViewInteractor()).setPermanent(true);
// set the initial interactor
mgrview.setInteractor(controlBar.getSelectInteractor());
// add this as selection listener
grapher.addManagerTreeSelectionListener(this);
// add he checkbox for same parameter everywhere
sameLayoutEverywhereCheckBox.setToolTipText(getString("sameLayoutCheckbox.Tooltip"));
sameLayoutEverywhereCheckBox.addItemListener(new ItemListener() {
Override
public void itemStateChanged(ItemEvent ev) {
if (ev.getStateChange() == ItemEvent.SELECTED)
setSameLayoutEverywhere(true);
else if (ev.getStateChange() == ItemEvent.DESELECTED)
setSameLayoutEverywhere(false);
}
});
// create the layout mode selector
JPanel modePanel = new JPanel();
modePanel.setLayout(new FlowLayout(FlowLayout.LEADING));
modePanel.add(new JLabel(getString("layoutModeSelector.Label")));
modePanel.add(layoutModeSelector);
layoutModeSelector.setToolTipText(getString("layoutModeSelector1.Tooltip"));
layoutModeSelector.addItemListener(new ItemListener() {
Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.DESELECTED)
return;
String item = (String) event.getItem();
if (item.equals(getString("layoutModeSelector.TreeMode")))
setLayoutMode(selectedObject, TREE);
else if (item.equals(getString("layoutModeSelector.RadialMode")))
setLayoutMode(selectedObject, RADIAL);
else if (item.equals(getString("layoutModeSelector.TipOverMode")))
setLayoutMode(selectedObject, TIP_OVER);
else if (item.equals(getString("layoutModeSelector.UniformMode")))
setLayoutMode(selectedObject, UNIFORM);
}
});
layoutModeSelector.addItem(getString("layoutModeSelector.TreeMode"));
layoutModeSelector.addItem(getString("layoutModeSelector.RadialMode"));
layoutModeSelector.addItem(getString("layoutModeSelector.TipOverMode"));
layoutModeSelector.addItem(getString("layoutModeSelector.UniformMode"));
// create the graph selector
JPanel graphPanel = new JPanel();
graphPanel.setLayout(new FlowLayout(FlowLayout.LEADING));
graphPanel.add(new JLabel(getString("fileChooser.Label")));
JComboBox<String> chooser;
graphPanel.add(chooser = new JComboBox<String>());
chooser.addItem("sample1");
chooser.addItem("sample2");
chooser.addItem("sample3");
chooser.setToolTipText(getString("fileChooser.Tooltip"));
chooser.addActionListener(new ActionListener() {
Override
public void actionPerformed(ActionEvent event) {
SuppressWarnings("unchecked")
JComboBox<String> comboBox = (JComboBox<String>) event.getSource();
String fileName = (String) comboBox.getSelectedItem();
loadGrapher(fileName);
layout(recursiveLayout, grapher);
}
});
// but rest together
panel.add(graphPanel);
panel.add(controlBar);
panel.add(modePanel);
panel.add(sameLayoutEverywhereCheckBox);
// create the panel on bottom
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BorderLayout());
getContentPane().add("South", bottomPanel);
// add the message line to the bottom panel
bottomPanel.add("South", msgLine);
msgLine.setEditable(false);
// set the component orientation according to the locale
Locale loc = IlvSwingUtil.getDefaultLocale();
ComponentOrientation co = ComponentOrientation.getOrientation(loc);
getContentPane().applyComponentOrientation(co);
// load a sample grapher
loadGrapher("sample1");
layout(recursiveLayout, grapher);
}
/**
* 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() {
RecursiveLayoutApp app = new RecursiveLayoutApp();
app.init();
JFrame frame = new JFrame(getString("Frame.Label"));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 600);
frame.getContentPane().add(app);
frame.setVisible(true);
app.start();
}
});
}
/**
* Starts the application.
*/
public void start() {
mgrview.fitTransformerToContent();
mgrview.repaint();
}
/**
* Load a sample IVL file.
*
* @param fileNameBase
* The base of the filename, excluding the path prefix and the
* extension suffix.
*/
private void loadGrapher(String fileNameBase) {
try {
showMessage(getString("FileReadingStartMessage"), fileNameBase);
grapher.deleteAll(false);
try {
grapher.read(IlvSwingUtil.getRelativeURL(this, "data/" + fileNameBase + ".ivl"));
} catch (Exception ex1) {
// This case occurs only when we pack the entire application into
// one jar including the data files, and start as application
grapher.read(getClass().getResource("data/" + fileNameBase + ".ivl"));
}
afterRead();
showMessage(getString("FileReadingDoneMessage"), fileNameBase);
} catch (Exception e) {
// e.printStackTrace();
showMessage(e.getMessage());
}
}
/**
* Postprocessing after reading a graph.
*/
private void afterRead() {
// and mark all except subgraphs as nonselectable.
IlvGraphicEnumeration objects = grapher.getObjects(true);
while (objects.hasMoreElements()) {
IlvGraphic g = objects.nextElement();
if (!(g instanceof IlvGrapher))
((IlvManager) g.getGraphicBag()).setSelectable(g, false);
}
// in internal provider mode, make sure each subgraph has a layout
if (!sameLayoutEverywhere)
createRecLayoutInIntProviderMode();
}
/**
* Sets whether the same layout is used everywhere.
*/
private void setSameLayoutEverywhere(boolean flag) {
if (flag == sameLayoutEverywhere)
return;
if (flag) {
// create a new layout in reference layout mode
createRecLayoutInReferenceMode(layoutMode);
layoutModeSelector.setToolTipText(getString("layoutModeSelector1.Tooltip"));
} else {
createRecLayoutInIntProviderMode();
layoutModeSelector.setToolTipText(getString("layoutModeSelector2.Tooltip"));
}
sameLayoutEverywhere = flag;
layout(recursiveLayout, grapher);
}
/**
* Sets the layout mode for a specific subgraph.
*/
private void setLayoutMode(Object subgraph, int mode) {
if (mode == layoutMode)
return;
if (sameLayoutEverywhere)
// ignore the input grapher, set the layout mode everywhere
createRecLayoutInReferenceMode(mode);
else if (subgraph == null || subgraph instanceof IlvGrapher)
// must have a recursive layout in internal provider mode.
// Set the layout of that specific grapher.
recursiveLayout.setLayout(subgraph, createSubLayout(mode));
layoutMode = mode;
layout(recursiveLayout, grapher);
}
/**
* Show layout mode at the checkbox.
*/
private void showLayoutModeAtCheckBox(int mode) {
// first set the mode, so the setSelectedIndex doesn't need to branch
// inside the inner of setLayoutMode
layoutMode = mode;
layoutModeSelector.setSelectedIndex(mode);
}
/**
* Returns the layout mode of a specific subgraph.
*/
private int getLayoutMode(Object subgraph) {
IlvGraphLayout layout = recursiveLayout.getLayout(subgraph);
if (layout == null)
return -1;
if (layout instanceof IlvUniformLengthEdgesLayout)
return UNIFORM;
if (layout instanceof IlvTreeLayout) {
IlvTreeLayout tlayout = (IlvTreeLayout) layout;
if (tlayout.getLayoutMode() == IlvTreeLayout.RADIAL)
return RADIAL;
if (tlayout.getGlobalAlignment() == IlvTreeLayout.TIP_OVER)
return TIP_OVER;
return TREE;
}
return -1;
}
/**
* Allocate a recursive layout in reference layout mode, so that the same
* layout style is applied everywhere.
*/
private void createRecLayoutInReferenceMode(int mode) {
if (recursiveLayout != null)
recursiveLayout.detach();
recursiveLayout = new IlvRecursiveLayout(createSubLayout(mode));
recursiveLayout.attach(adapter);
}
/**
* Allocate a recursive layout in internal provider mode, so that different
* layout styles can be applied to different subgraphs.
*/
private void createRecLayoutInIntProviderMode() {
// get the old reference layout if any, and detach
IlvGraphLayout oldreflayout = null;
if (recursiveLayout != null) {
oldreflayout = recursiveLayout.getReferenceLayout();
recursiveLayout.detach();
}
// create a new layout in internal provider mode
recursiveLayout = new IlvRecursiveLayout();
recursiveLayout.attach(adapter);
// set the layout style for all subgraphs
if (oldreflayout != null)
recursiveLayout.setLayout(null, oldreflayout.copy(), true, true);
else
recursiveLayout.setLayout(null, new IlvTreeLayout(), true, true);
}
/**
* Returns a new graph layout instance depending on the input mode. This is
* used as sublayout of the recursive layout.
*/
private IlvGraphLayout createSubLayout(int mode) {
switch (mode) {
case TREE:
IlvTreeLayout l0 = new IlvTreeLayout();
l0.setGlobalLinkStyle(IlvTreeLayout.ORTHOGONAL_STYLE);
l0.setParentChildOffset(60);
l0.setFlowDirection(IlvDirection.Bottom);
return l0;
case RADIAL:
IlvTreeLayout l1 = new IlvTreeLayout();
l1.setLayoutMode(IlvTreeLayout.RADIAL);
return l1;
case TIP_OVER:
IlvTreeLayout l2 = new IlvTreeLayout();
l2.setGlobalAlignment(IlvTreeLayout.TIP_OVER);
l2.setFlowDirection(IlvDirection.Bottom);
return l2;
case UNIFORM:
IlvUniformLengthEdgesLayout l3 = new IlvUniformLengthEdgesLayout();
l3.setPreferredLinksLength(60);
l3.setRespectNodeSizes(true);
return l3;
}
return new IlvTreeLayout();
}
/**
* Performs the layout of the graph.
*/
private void layout(IlvRecursiveLayout layout, IlvGrapher grapher) {
// the layout report instance; this is an object in which
// the layout algorithm stores information about its behavior
IlvGraphLayoutReport layoutReport = null;
showMessage(getString("LayoutStartMessage"));
// initialize the iteration listener
layoutListener.initialize();
try {
// perform the layout and get the layout report
layoutReport = layout.performLayout(false, false);
// print the code from the layout report
showMessage(getString("LayoutDoneMessage"),
layoutReport.codeToString(layoutReport.getCode(), IlvLocaleUtil.getCurrentULocale()));
} catch (IlvGraphLayoutException e) {
// e.printStackTrace();
showMessage(e.getMessage());
} finally {
if (layoutReport != null && layoutReport.getCode() != IlvGraphLayoutReport.NOT_NEEDED) {
// show all
mgrview.fitTransformerToContent();
mgrview.repaint();
}
}
}
/**
* Displays a message.
*/
void showMessage(String message) {
// the message is displayed in the message line
msgLine.setText(message);
msgLine.paintImmediately(0, 0, msgLine.getWidth(), msgLine.getHeight());
}
void showMessage(String msgformat, Object val) {
Object[] args = { val };
showMessage(MessageFormat.format(msgformat, args));
}
/**
* Returns a string.
*/
static String getString(String key) {
return IlvResourceUtil.getString(key, RecursiveLayoutApp.class, IlvLocaleUtil.getCurrentLocale());
}
/**
* This method is called when the selection changes in a manager.
*/
Override
public void selectionChanged(ManagerSelectionChangedEvent event) {
IlvGraphic g = event.getGraphic();
IlvManager m = event.getManager();
if (m.isSelected(g)) {
IlvGraphic previousSelectedObject = selectedObject;
selectedObject = g;
if (previousSelectedObject != null && previousSelectedObject != g) {
IlvManager m1 = (IlvManager) previousSelectedObject.getGraphicBag();
if (m1.isSelected(previousSelectedObject))
m1.setSelected(previousSelectedObject, false, true);
}
if (g instanceof IlvGrapher)
showLayoutModeAtCheckBox(getLayoutMode(g));
} else if (g == selectedObject) {
selectedObject = null;
showLayoutModeAtCheckBox(getLayoutMode(null));
}
}
// -------------------------------------------------------------------------
/**
* A graph layout iteration listener. Implementing the interface
* GraphLayoutEventListener gives you the possibility to receive during the
* layout information about the behavior of the layout algorithm. This
* information is contained in the graph layout event.
*
* In our case, we will simply print the percentage completed by the layout
* each time the method layoutStepPerformed is called, that is after each
* iteration of the Tree Layout algorithm.
*/
class LayoutIterationListener implements GraphLayoutEventListener {
private float oldPercentage;
/**
* This method is automatically called by the layout algorithm.
*/
Override
public void layoutStepPerformed(GraphLayoutEvent event) {
float percentage = event.getLayoutReport().getPercentageComplete();
if (percentage != oldPercentage) {
showMessage(getString("percentage.Label"), new Float(percentage));
oldPercentage = percentage;
}
}
/**
* Initialize the listener by reseting the toShow variable. This method must
* be called before the layout is started.
*/
void initialize() {
oldPercentage = -1.0f;
}
}
}