/* * Licensed Materials - Property of Perforce Software, Inc. * © Copyright Perforce Software, Inc. 2014, 2021 * © Copyright IBM Corp. 2009, 2014 * © Copyright ILOG 1996, 2009 * All Rights Reserved. * * Note to U.S. Government Users Restricted Rights: * The Software and Documentation were developed at private expense and * are "Commercial Items" as that term is defined at 48 CFR 2.101, * consisting of "Commercial Computer Software" and * "Commercial Computer Software Documentation", as such terms are * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4, * as applicable. */ 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; } } }