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

import ilog.views.IlvManager;
import ilog.views.IlvGrapher;
import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.IlvLinkImage;
import ilog.views.IlvDirection;
import ilog.views.IlvHandlesSelection;
import ilog.views.IlvManagerView;
import ilog.views.IlvTransformer;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvApplyObject;
import ilog.views.IlvExpandCollapseUtil;

import ilog.views.interactor.IlvSelectInteractor;
import ilog.views.interactor.IlvZoomViewInteractor;
import ilog.views.interactor.IlvExpandCollapseInteractor;

import ilog.views.event.ManagerSelectionChangedEvent;

import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.swing.IlvJManagerViewControlBar;

import ilog.views.graphlayout.IlvGraphLayout;
import ilog.views.graphlayout.IlvGraphLayoutReport;
import ilog.views.graphlayout.IlvGraphLayoutException;
import ilog.views.graphlayout.IlvGrapherAdapter;
import ilog.views.graphlayout.IlvGraphModel;
import ilog.views.graphlayout.IlvNodeSideFilter;
import ilog.views.graphlayout.hierarchical.IlvHierarchicalLayout;
import ilog.views.graphlayout.link.IlvLinkLayout;

import ilog.views.util.IlvImageUtil;
import ilog.views.util.IlvProductUtil;
import ilog.views.util.IlvResourceUtil;
import ilog.views.util.IlvLocaleUtil;

import ilog.views.util.swing.IlvSwingUtil;

import java.util.*;
import java.text.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * This is a very simple applet/application that shows various
 * policies when expanding and collapsing graphs.
 * It shows how to use the expand utilities in applications that are not based
 * on CSS styling.
 */
public class ExpandCollapseApplet extends JApplet
{
  static {
    // This applet is designed to run only with default resource bundle
    // and various selected other resource bundles.
    // Setting the available resource suffixes avoids that the applet
    // tries to load resource bundles for other locales over the net,
    // even if the current locale of the browser is different.
    if (IlvResourceUtil.isInApplet())
      IlvResourceUtil.setAvailableResourceSuffixes("", "_ja");

    // Test code whether the demo works in RTL locales
    // Locale newLocale = new Locale("he");
    // IlvSwingUtil.setDefaultLocale(newLocale);
  }

  {
    // This sample uses JViews Diagrammer features. When deploying an
    // application that includes this code, you need to be in possession
    // of a JViews Diagrammer Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(
        IlvProductUtil.JViews_Diagrammer_Deployment);
  }

  /** The grapher */
  IlvGrapher grapher = new IlvGrapher();

  /** The view of the grapher */
  IlvManagerView mgrview = new IlvManagerView(grapher);

  /** An instance of the Hierarchical Layout algorithm */
  IlvHierarchicalLayout hierarchicalLayout = new IlvHierarchicalLayout();

  /** An instance of the Link Layout algorithm */
  IlvLinkLayout linkLayout = new IlvLinkLayout();

  /** A node side filter for the Link Layout algorithm */
  IlvNodeSideFilter nodeSideFilter = new IlvNodeSideFilter() {
    public boolean accept(IlvGraphModel graphModel,
                          Object link,
                          boolean origin,
                          Object node,
                          int side)
    {
      if (origin)
        return side == IlvDirection.Right;
      else
        return side == IlvDirection.Left;
    }
  };

  /** A text field to display messages */
  JTextField msgLine = new JTextField();

  /** The combo box for the link layout */
  JComboBox linkLayoutSelector = new JComboBox();

  /** The adjustment mode */
  String actModeString = "";
  int mode;

  /** Whether incremental layout is required */
  boolean incrementalLayout = false;

  /** Whether we allow link layout */
  boolean allowLinkLayout = true;

  /** Whether a separate link layout is required */
  String linkLayoutSelectionString = getString("linkLayoutSelector.NoneMode");

  /** A custom adjustment policy */
  PolicyWithLinkLayout customPolicy = new PolicyWithLinkLayout();

  /**
   * Initializes the applet/application.
   */
  public void init()
  {
    showMessage(getString("InitMessage"));

    super.init();

    // attach the grapher to the layout instances
    IlvGrapherAdapter adapter = new IlvGrapherAdapter(grapher);
    hierarchicalLayout.attach(adapter);
    linkLayout.attach(adapter);

    // change the default option for the links style
    hierarchicalLayout.setGlobalLinkStyle(IlvHierarchicalLayout.ORTHOGONAL_STYLE);

    // change the default flow direction
    hierarchicalLayout.setFlowDirection(IlvDirection.Right);

    // 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.setZoomFactorRange(0.02, 10.0);
    mgrview.setWheelZoomingEnabled(true);
    scrollManView.setWheelScrollingEnabled(true);

    // create the standard control bar
    IlvJManagerViewControlBar controlBar = new IlvJManagerViewControlBar();
    controlBar.setView(mgrview);

    IlvExpandCollapseInteractor inter =
      new IlvExpandCollapseInteractor() {
         protected void expandOrCollapse(IlvGraphic obj) {
           if (obj instanceof IlvManager) {
             expandOrCollapseImpl((IlvManager)obj);
           } else {
             super.expandOrCollapse(obj);
           }
         }
      };

    controlBar.setSelectInteractor(inter);
    Image selectIconImage =  getImage("gif/Expand.gif");
    controlBar.setSelectIcon(new ImageIcon(selectIconImage));
    controlBar.setSelectToolTipText(getString("expandButton.Tooltip"));

    // modify the interactors such that the demo looks better
    ((IlvZoomViewInteractor)controlBar.getZoomViewInteractor()).setPermanent(true);

    // set the initial interactor
    mgrview.setInteractor(controlBar.getSelectInteractor());

    // create the bottom panel with the layout buttons and the message line
    JPanel bottomPanel = new JPanel();
    bottomPanel.setLayout(new BorderLayout());
    bottomPanel.add(createParameterPanel(), BorderLayout.CENTER);
    bottomPanel.add(msgLine, BorderLayout.SOUTH);
    msgLine.setEditable(false);

    // put manager view, top panel and bottom panel together
    getContentPane().setLayout(new BorderLayout(0, 0));
    getContentPane().add(scrollManView, BorderLayout.CENTER);
    getContentPane().add(controlBar, BorderLayout.NORTH);
    getContentPane().add(bottomPanel, BorderLayout.SOUTH);

    // set the component orientation according to the locale
    Locale loc = IlvSwingUtil.getDefaultLocale();
    ComponentOrientation co = ComponentOrientation.getOrientation(loc);
    getContentPane().applyComponentOrientation(co);

    // show all
    mgrview.fitTransformerToContent(true);
  }

  /**
   * Called when this applet is being reclaimed in order to destroy
   * any resources that it has allocated.
   */
  public void destroy()
  {
    super.destroy();

    // This method is intended to workaround memory management issues
    // in the Sun JRE. Please refer to the method documentation for more
    // details and a description of the known issues.
    IlvSwingUtil.cleanupApplet();
  }

  /**
   * Creates the parameter panel.
   */
  private JPanel createParameterPanel()
  {
    JPanel panel = new JPanel();
    GridBagLayout gridbag = new GridBagLayout();
    GridBagConstraints c = new GridBagConstraints();
    c.fill = GridBagConstraints.HORIZONTAL;
    c.anchor = GridBagConstraints.LINE_END;
    panel.setLayout(gridbag);

    // expansion adjustment mode parameter
    c.gridx = 0;
    c.gridy = 0;
    panel.add(new JLabel(getString("expandModeSelector.Label")), c);
    c.gridx = 1;
    panel.add(new JLabel(" "), c);
    c.gridx = 2;
    JComboBox adjustmentModeSelector = new JComboBox();
    panel.add(adjustmentModeSelector, c);
    adjustmentModeSelector.addItem(getString("expandModeSelector.NoneMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalWithLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalWithoutLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalRecWithLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalRecWithoutLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalStableWithLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.OrthogonalStableWithoutLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.RadialWithLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.RadialWithoutLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.RadialRecWithLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.RadialRecWithoutLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.RadialStableWithLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.RadialStableWithoutLinksMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.IncrLayoutMode"));
    adjustmentModeSelector.addItem(getString("expandModeSelector.CustomMode"));
    adjustmentModeSelector.setSelectedIndex(5);
    setAdjustmentMode((String)adjustmentModeSelector.getSelectedItem());
    adjustmentModeSelector.setToolTipText(getString("expandModeSelector.Tooltip"));
    adjustmentModeSelector.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent event) {
          JComboBox comboBox = (JComboBox)event.getSource();
          String modeString = (String)comboBox.getSelectedItem();
          setAdjustmentMode(modeString);
        }
      });

    // link layout mode parameter
    c.gridx = 0;
    c.gridy = 1;
    panel.add(new JLabel(getString("linkLayoutSelector.Label")), c);
    c.gridx = 1;
    panel.add(new JLabel(" "), c);
    c.gridx = 2;
    panel.add(linkLayoutSelector, c);
    linkLayoutSelector.addItem(getString("linkLayoutSelector.NoneMode"));
    linkLayoutSelector.addItem(getString("linkLayoutSelector.HierMode"));
    linkLayoutSelector.addItem(getString("linkLayoutSelector.GenericMode"));
    linkLayoutSelector.setSelectedIndex(0);
    linkLayoutSelector.setToolTipText(getString("linkLayoutSelector.Tooltip"));
    linkLayoutSelector.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent event) {
          JComboBox comboBox = (JComboBox)event.getSource();
          String modeString = (String)comboBox.getSelectedItem();
          setLinkLayoutMode(modeString);
        }
      });

    return panel;
  }

  /**
   * Sets the adjustment mode.
   */
  private void setAdjustmentMode(String modeString)
  {
    if (actModeString.equals(modeString)) return;

    actModeString = modeString;

    // to test a mode, it is better to start with a clean situation,
    // hence load the graph new.

    incrementalLayout = false;

    if (modeString.equals(getString("expandModeSelector.NoneMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.NONE;
      allowLinkLayout = true;
    } else if (modeString.equals(getString("expandModeSelector.OrthogonalWithLinksMode"))) {
      // load flatter variant
      loadGrapher("graph2");
      mode = IlvExpandCollapseUtil.ORTHOGONAL_WITH_LINKS;
      allowLinkLayout = false;
    } else if (modeString.equals(getString("expandModeSelector.OrthogonalWithoutLinksMode"))) {
      // load flatter variant
      loadGrapher("graph2");
      mode = IlvExpandCollapseUtil.ORTHOGONAL_WITHOUT_LINKS;
      allowLinkLayout = true;
    } else if (modeString.equals(getString("expandModeSelector.OrthogonalRecWithLinksMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.ORTHOGONAL_RECURSIVE_WITH_LINKS;
      allowLinkLayout = false;
    } else if (modeString.equals(getString("expandModeSelector.OrthogonalRecWithoutLinksMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.ORTHOGONAL_RECURSIVE_WITHOUT_LINKS;
      allowLinkLayout = true;
    } else if (modeString.equals(getString("expandModeSelector.OrthogonalStableWithLinksMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.ORTHOGONAL_STABLE_WITH_LINKS;
      allowLinkLayout = false;
    } else if (modeString.equals(getString("expandModeSelector.OrthogonalStableWithoutLinksMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.ORTHOGONAL_STABLE_WITHOUT_LINKS;
      allowLinkLayout = true;
    } else if (modeString.equals(getString("expandModeSelector.RadialWithLinksMode"))) {
      // load flatter variant
      loadGrapher("graph2");
      mode = IlvExpandCollapseUtil.RADIAL_WITH_LINKS;
      allowLinkLayout = false;
    } else if (modeString.equals(getString("expandModeSelector.RadialWithoutLinksMode"))) {
      // load flatter variant
      loadGrapher("graph2");
      mode = IlvExpandCollapseUtil.RADIAL_WITHOUT_LINKS;
      allowLinkLayout = true;
    } else if (modeString.equals(getString("expandModeSelector.RadialRecWithLinksMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.RADIAL_RECURSIVE_WITH_LINKS;
      allowLinkLayout = false;
    } else if (modeString.equals(getString("expandModeSelector.RadialRecWithoutLinksMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.RADIAL_RECURSIVE_WITHOUT_LINKS;
      allowLinkLayout = true;
    } else if (modeString.equals(getString("expandModeSelector.RadialStableWithLinksMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.RADIAL_STABLE_WITH_LINKS;
      allowLinkLayout = false;
    } else if (modeString.equals(getString("expandModeSelector.RadialStableWithoutLinksMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.RADIAL_STABLE_WITHOUT_LINKS;
      allowLinkLayout = true;
    } else if (modeString.equals(getString("expandModeSelector.IncrLayoutMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.NONE;
      incrementalLayout = true;
      allowLinkLayout = false;
    } else if (modeString.equals(getString("expandModeSelector.CustomMode"))) {
      // load deeply nested variant
      loadGrapher("graph1");
      mode = IlvExpandCollapseUtil.STABLE;
      incrementalLayout = false;
      allowLinkLayout = false;
    }

    linkLayoutSelector.setEnabled(allowLinkLayout);

    showMessage(getString("ExpandModeStatusMessage"), modeString);
  }

  /**
   * Sets the link layout mode.
   */
  private void setLinkLayoutMode(String modeString)
  {
    linkLayoutSelectionString = modeString;
    showMessage(getString("LinkLayoutStatusMessage"), modeString);
  }

  /**
   * Expand or collapse a manager.
   */
  private void expandOrCollapseImpl(IlvManager manager)
  {
    if (manager.getParent() == null) return;

    boolean collapse = !manager.isCollapsed();

    // when expanding and using incremental layout, the node bounds get bigger.
    // The incremental layout works better if it knows about the smaller node
    // bounds before expanding.

    if (incrementalLayout && !collapse) {
      IlvManager node = manager;
      IlvManager parent = manager.getParent();
      while (parent != null) {
        IlvHierarchicalLayout layoutOfNode = (IlvHierarchicalLayout)
          hierarchicalLayout.getRecursiveLayout().getLayout(parent);
        IlvRect bbox = layoutOfNode.getGraphModel().boundingBox(node);
        layoutOfNode.setIncrementalNodeBoxForExpand(node, bbox);
        node = parent;
        parent = node.getParent();
      }
    }

    if (allowLinkLayout &&
        !linkLayoutSelectionString.equals(getString("linkLayoutSelector.NoneMode"))) {
      // the expand/collapse behaves more stable if all links are straightened
      // before expand/collapse. Since there is a link layout afterwards anyway,
      // the links get orthogonal anyway.
      straightenAllLinks();
    }

    // now expand or collapse

    IlvTransformer t = manager.getParent().getDrawingTransformer(mgrview); 
    if (mode == IlvExpandCollapseUtil.STABLE) {
      // custom adjustment strategy
      IlvExpandCollapseUtil.expandOrCollapse(manager, customPolicy, mode, collapse,
                                             t, true);
    } else {
      // default adjustment strategy
      IlvExpandCollapseUtil.expandOrCollapse(manager, mode, collapse, t, true);
    }

    // if the mode says we want an incremental hierarchical layout, do this now

    if (incrementalLayout) {
      hierarchicalLayout.setIncrementalMode(true);
      hierarchicalLayout.setIncrementalAbsoluteLevelPositioning(false);
      hierarchicalLayout.setGlobalIncrementalNodeMovementMode(
        IlvHierarchicalLayout.FREE_MODE);
      try {
        hierarchicalLayout.performLayout(true, true, true);
      } catch (IlvGraphLayoutException ex) {
        ex.printStackTrace();
      }
    }

    // if an additional link layout is enabled, do this now.

    if (allowLinkLayout) {
      // only if we do no incremental hierarchical layout, we might need
      // a link layout
      if (linkLayoutSelectionString.equals(getString("linkLayoutSelector.HierMode"))) {
        hierarchicalLayout.setIncrementalMode(true);
        hierarchicalLayout.setIncrementalAbsoluteLevelPositioning(true);
        hierarchicalLayout.setGlobalIncrementalNodeMovementMode(
          IlvHierarchicalLayout.FIXED_MODE);
        try {
          hierarchicalLayout.performLayout(true, true, true);
        } catch (IlvGraphLayoutException ex) {
          ex.printStackTrace();
        }
      } else if (linkLayoutSelectionString.equals(getString("linkLayoutSelector.GenericMode"))) {
        linkLayout.setIncrementalMode(true);
        linkLayout.setNodeSideFilter(nodeSideFilter);
        try {
          linkLayout.performLayout(true, true, true);
        } catch (IlvGraphLayoutException ex) {
          ex.printStackTrace();
        }
      }
    }
  }

  /**
   * 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"));
      }
      // the transformer may have been modified, so
      // we set the identity transformer
      mgrview.fitTransformerToContent();
      mgrview.repaint();
      showMessage(getString("FileReadingDoneMessage"), fileNameBase);
    } catch (Exception e) {
      // e.printStackTrace();
      showMessage(e.getMessage());
    }
  }

  /**
   * 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));
  }

  void showMessage(String msgformat, Object val1, Object val2)
  {
    Object[] args = { val1 , val2 };
    showMessage(MessageFormat.format(msgformat, args));
  }

  /**
   * Returns a string.
   */
  static String getString(String key)
  {
    return IlvResourceUtil.getString(key, ExpandCollapseApplet.class,
                                     IlvLocaleUtil.getCurrentLocale());
  }

  /**
   * Reads an image. It first reads from the jar itself, and then tries to read
   * fromfile.
   */
  public Image getImage(String fileName)
  {
    Image image = null;
    // try to get it from jar
    try {
      image = IlvImageUtil.getImageFromFile(getClass(), fileName);
    } catch (IOException e) {
      // System.err.println("Image not found: " + fileName + " exception: " + e.getMessage());
      showMessage(getString("ImageNotFoundErrorMessage") + "<br>", fileName, e.getMessage());
    }

    // if couldn't get it from the jar...
    if (image == null)
      image = getToolkit().getImage(fileName);

    // if still null...
    if (image == null) {
      // System.err.println("Could not get image " + fileName + "!");
      showMessage(getString("MissingImageErrorMessage") + "<br>", fileName);
    }
    return image;
  }

  /**
   * Straighten all links in the grapher.
   */
  private void straightenAllLinks()
  {
    IlvGraphicEnumeration e = grapher.getObjects(true);
    grapher.applyToObjects(e, new IlvApplyObject() {
      public void apply(IlvGraphic g, Object arg) {
        if (g instanceof IlvLinkImage) {
          IlvLinkImage link = (IlvLinkImage)g;
          link.setIntermediateLinkPoints(null, 0, 0);
        }
      }
    }, null, true);
  }

  /**
   * Allows you to run the demo as a standalone application.
   */
  public static void main(String[] arg)
  {
    // Sun recommends that to put the entire GUI initialization into the
    // AWT thread
    SwingUtilities.invokeLater(
      new Runnable() {
        public void run() {
          ExpandCollapseApplet applet = new ExpandCollapseApplet();
          applet.init();

          JFrame frame = new JFrame(getString("Frame.Label"));
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.setSize(600, 600);
          frame.getContentPane().add(applet);
          frame.setVisible(true);
        }
      });
  }

  // -------------------------------------------------------------------------------

  /**
   * A custom adjustment policy that runs a hierarchical link layout inside
   * the adjustment (instead of running it after the expand/collapse).
   * If a link layout is needed, this is in general the best approach that gives
   * the best results, but it is also the slowest approach in stable mode.
   */
  class PolicyWithLinkLayout implements IlvExpandCollapseUtil.CustomAdjustmentPolicy
  {
    public void apply(IlvManager parent,
                      IlvManager child,
                      IlvRect oldBounds,
                      IlvRect newBounds,
                      IlvTransformer t,
                      boolean redraw)
    {
      // do an orthogonal expansion (very similar to orthWithoutLinksPolicy)

      // first move the child to the center of the old bounds. This is
      // already done by setCollapsed, most of the time, except that it is
      // done with respect to null transformer and we really need it with
      // respect to transformer t. Hence do it again.

      float deltaX = 0.5f * (oldBounds.width - newBounds.width);
      float deltaY = 0.5f * (oldBounds.height - newBounds.height);

      IlvPoint p = new IlvPoint(oldBounds.x + deltaX, oldBounds.y + deltaY);
      if (t != null)
        t.inverse(p);

      parent.moveObject(child, p.x, p.y, redraw);

      // shift all nodes in x or y direction

      float minX = oldBounds.x;
      float maxX = oldBounds.x + oldBounds.width;
      float centerX = oldBounds.x + 0.5f * oldBounds.width;
      float minY = oldBounds.y;
      float maxY = oldBounds.y + oldBounds.height;
      float centerY = oldBounds.y + 0.5f * oldBounds.height;

      IlvGraphicEnumeration e = parent.getObjects();
      while (e.hasMoreElements()) {
        IlvGraphic g = e.nextElement();
        if (g == child) continue;
        if (g instanceof IlvLinkImage) continue;
        IlvRect bbox = g.boundingBox(t);
        p.setLocation(bbox.x, bbox.y);
        float cx = bbox.x + 0.5f * bbox.width;
        float cy = bbox.y + 0.5f * bbox.height;
        if (cx > maxX)
          p.x -= deltaX;
        else if (cx < minX)
          p.x += deltaX;
        else 
          p.x -= (deltaX * 2 * (cx - centerX) / oldBounds.width);
        if (cy > maxY)
          p.y -= deltaY;
        else if (cy < minY)
          p.y += deltaY;
        else
          p.y -= (deltaY * 2 * (cy - centerY) / oldBounds.height);
        if (t != null)
          t.inverse(p);
        parent.moveObject(g, p.x, p.y, redraw);
      }

      // do a link layout (using the hierarchical layout) on the parent
      IlvHierarchicalLayout layout = new IlvHierarchicalLayout();
      layout.attach((IlvGrapher)parent);
      layout.setGlobalLinkStyle(IlvHierarchicalLayout.ORTHOGONAL_STYLE);
      layout.setFlowDirection(IlvDirection.Right);
      layout.setIncrementalMode(true);
      layout.setIncrementalAbsoluteLevelPositioning(true);
      layout.setGlobalIncrementalNodeMovementMode(IlvHierarchicalLayout.FIXED_MODE);
      try {
        // perform layout only on parent, not recursively on children
        layout.performLayout(true, true);
      } catch (IlvGraphLayoutException ex) {
        ex.printStackTrace();
      }
      layout.detach();
    } 
  }

}