/*
 * 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.
 */

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.geom.RoundRectangle2D;

import javax.swing.AbstractAction;
import javax.swing.Action;
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.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

import ilog.views.IlvDefaultManagerFrame;
import ilog.views.IlvGrapher;
import ilog.views.IlvGraphic;
import ilog.views.IlvHandlesSelection;
import ilog.views.IlvLinkImage;
import ilog.views.IlvManagerView;
import ilog.views.IlvManagerViewInteractor;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.accelerator.IlvDeleteSelectionAccelerator;
import ilog.views.accelerator.IlvFitToSizeAccelerator;
import ilog.views.accelerator.IlvIdentityAccelerator;
import ilog.views.accelerator.IlvRotateAccelerator;
import ilog.views.accelerator.IlvScrollDownAccelerator;
import ilog.views.accelerator.IlvScrollLeftAccelerator;
import ilog.views.accelerator.IlvScrollRightAccelerator;
import ilog.views.accelerator.IlvScrollUpAccelerator;
import ilog.views.accelerator.IlvSelectAllAccelerator;
import ilog.views.accelerator.IlvZoomInAccelerator;
import ilog.views.accelerator.IlvZoomOutAccelerator;
import ilog.views.event.InteractorChangedEvent;
import ilog.views.event.InteractorListener;
import ilog.views.graphic.IlvGeneralPath;
import ilog.views.graphic.IlvPolylineLinkImage;
import ilog.views.graphic.IlvRectangle;
import ilog.views.graphic.IlvText;
import ilog.views.graphic.composite.IlvCompositeGraphic;
import ilog.views.graphic.composite.IlvEventMap;
import ilog.views.graphic.composite.layout.IlvAttachmentConstraint;
import ilog.views.graphic.composite.layout.IlvAttachmentLayout;
import ilog.views.graphic.composite.layout.IlvAttachmentLocation;
import ilog.views.graphic.composite.layout.IlvStackerLayout;
import ilog.views.graphic.composite.objectinteractor.IlvCompositeContext;
import ilog.views.graphic.composite.objectinteractor.IlvCompositeInteractor;
import ilog.views.interactor.IlvMakePolyLinkInteractor;
import ilog.views.interactor.IlvSelectInteractor;
import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.util.swing.IlvDecimalNumberField;
import ilog.views.util.swing.IlvJComboBox;
import ilog.views.util.swing.IlvSwingUtil;

/**
 * This sample shows how to use the utility method 
 * <code>ilog.views.IlvGrapher#setVisibleBranch(IlvGraphic, int, boolean, boolean)</code>.
 */
SuppressWarnings("serial")
public class ShowHideBranch extends JRootPane {

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

  IlvGrapher grapher;
  IlvManagerView mgrview;

  /** The panel containing buttons. */
  JPanel options;

  /** Array of buttons. */
  InteractorButton[] interactorButtons;

  private double minpos = 0;

  private int startLevel = 0;
  private int endLevel = -1;
  boolean intergraphLinks = false;
  boolean treeExtIntergraphLinks = false;

  /** The tree is created with this number of children for each node. */
  private static final int NUMBER_OF_CHILDREN = 2;

  /** How large the nodes should be. */
  private static final double NODE_WIDTH = 45;
  private static final double NODE_HEIGHT = 35;

  /** For laying out the nodes **/
  private static final int MIN_XSPACE = 60;
  private static final int MIN_YSPACE = 20;

  /** How thick the links should be. */
  private static final double LINK_WIDTH = 2;

  /** 
   * Initializes the app.
   */
  public void init() {
    getContentPane().setLayout(new BorderLayout(0, 0));

    // Creates the grapher.
    grapher = new IlvGrapher();

    // Creates the view of the grapher.
    mgrview = new IlvManagerView(grapher, null);
    mgrview.setKeepingAspectRatio(true);
    mgrview.setAntialiasing(true);
    mgrview.addInteractorListener(new InteractorListener() {
      Override
      public void interactorChanged(InteractorChangedEvent evt) {
        IlvManagerViewInteractor newI = evt.getNewValue();
        for (int i = 0; i < interactorButtons.length; i++) {
          boolean select = interactorButtons[i].getInteractor() == newI;
          if (select != interactorButtons[i].isSelected()) {
            interactorButtons[i].removeItemListener(interactorButtons[i]);
            interactorButtons[i].setSelected(select);
            interactorButtons[i].addItemListener(interactorButtons[i]);
          }
        }
      }
    });

    // Setting parameters for selection handles.
    IlvHandlesSelection.defaultHandleColor = Color.black;
    IlvHandlesSelection.defaultHandleBackgroundColor = Color.white;
    IlvHandlesSelection.defaultHandleShape = IlvHandlesSelection.SQUARE_SHAPE;

    // Adds the graphic objects to the grapher.
    minpos = 0;
    addSubtree(0, 4, grapher);

    // Adds some accelerators.
    grapher.addAccelerator(new IlvIdentityAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_I, 0));
    grapher.addAccelerator(
        new IlvZoomOutAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_U, KeyEvent.CTRL_MASK));
    grapher.addAccelerator(
        new IlvZoomInAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_Z, KeyEvent.CTRL_MASK));
    grapher.addAccelerator(new IlvFitToSizeAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_F, 0));
    grapher.addAccelerator(new IlvRotateAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_R, 0));
    grapher.addAccelerator(
        new IlvSelectAllAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_A, KeyEvent.CTRL_MASK));
    grapher.addAccelerator(new IlvScrollUpAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_UP, 0));
    grapher.addAccelerator(new IlvScrollDownAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_DOWN, 0));
    grapher
        .addAccelerator(new IlvScrollRightAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_RIGHT, 0));
    grapher.addAccelerator(new IlvScrollLeftAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_LEFT, 0));


    IlvJScrollManagerView scview = new IlvJScrollManagerView(mgrview);

    mgrview.setWheelZoomingEnabled(true);
    scview.setWheelScrollingEnabled(true);

    getContentPane().add(scview, BorderLayout.CENTER);

    // Allows the user to delete nodes by pressing the "del" key.
    grapher.addAccelerator(
        new IlvDeleteSelectionAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_DELETE, 0));

    // Creates the options panel.
    JPanel optionsPanel;
    getContentPane().add(optionsPanel = new JPanel(), BorderLayout.WEST);
    optionsPanel.setLayout(new BorderLayout(0, 0));
    options = new JPanel();
    optionsPanel.add(options, BorderLayout.NORTH);
    options.setLayout(new GridLayout(0, 1, 1, 1));
    initOptionsPanel();
  }

  /**
   * Adds one interactor button.
   */
  void addButtonInteractor(int index, String text, String tooltip, IlvManagerViewInteractor inter) {
    interactorButtons[index] = new InteractorButton(text, inter, tooltip, this);

    options.add(interactorButtons[index]);
    interactorButtons[index].setSize(20, 20);
  }

  /**
   * Adds all the interactor buttons and options GUI.
   */
  void initOptionsPanel() {

    boolean opaque = true; // set the options in opaque mode
    int n = 0;
    interactorButtons = new InteractorButton[1];
    IlvSelectInteractor selInter = new IlvSelectInteractor();
    selInter.setOpaqueDragSelection(opaque);
    selInter.setOpaqueMove(opaque);
    selInter.setOpaqueResize(opaque);
    selInter.setOpaquePolyPointsEdition(opaque);
    selInter.setObjectInteractorEnabled(true);

    IlvMakePolyLinkInteractor makePolyLink = new IlvMakePolyLinkInteractor() {
      Override
      public IlvLinkImage createObject(IlvGraphic from, IlvGraphic to, IlvPoint[] points,
          boolean oriented) {
        IlvLinkImage link = super.createObject(from, to, points, oriented);
        link.setLineWidth(LINK_WIDTH);
        link.setOriented(true);
        return link;
      }
    };
    makePolyLink.setOpaqueMode(opaque);
    makePolyLink.setPermanent(false);
    makePolyLink.setForeground(Color.blue);
    addButtonInteractor(n++, "Create Link", "Allows the creation of a polyline links",
        makePolyLink);

    // ==============
    // level

    final IlvDecimalNumberField startLevelField = new IlvDecimalNumberField(0, 1000, false);
    startLevelField.setText("0");
    startLevelField.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent event) {
        String text = startLevelField.getText();
        Integer levelInt = Integer.valueOf(text);
        startLevel = levelInt.intValue();
      }
    });
    options.add(new JLabel("Start Level:"));
    options.add(startLevelField);

    final IlvDecimalNumberField endLevelField = new IlvDecimalNumberField(-1, 1000, false);
    endLevelField.setText("-1");
    endLevelField.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent event) {
        String text = endLevelField.getText();
        Integer levelInt = Integer.valueOf(text);
        endLevel = levelInt.intValue();
      }
    });
    options.add(new JLabel("End Level:"));
    options.add(endLevelField);


    // ============
    // Intergraph links

    SuppressWarnings("unchecked")
    final JComboBox<String> linksModeBox = new IlvJComboBox();
    linksModeBox.addItem("Normal links");
    linksModeBox.addItem("Explicit intergraph links");
    linksModeBox.addItem("Implicit intergraph links");
    linksModeBox.addItem("Implicit subgraphs");
    linksModeBox.setEnabled(false);
    linksModeBox.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent event) {
        SuppressWarnings("unchecked")
        JComboBox<String> comboBox = (JComboBox<String>) event.getSource();
        String sel = (String) comboBox.getSelectedItem();
        if (sel.equals("Normal links")) {
          intergraphLinks = false;
          treeExtIntergraphLinks = false;
        } else if (sel.equals("Explicit intergraph links")) {
          intergraphLinks = true;
          treeExtIntergraphLinks = false;
        } else if (sel.equals("Implicit intergraph links")) {
          intergraphLinks = true;
          treeExtIntergraphLinks = true;
        } else if (sel.equals("Implicit subgraphs")) {
          intergraphLinks = false;
          treeExtIntergraphLinks = true;
        }
      }
    });

    // ============
    // nested

    final JCheckBox nestedCheckBox = new JCheckBox("Nested Graph", false);
    nestedCheckBox.addItemListener(new ItemListener() {
      Override
      public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED) {
          minpos = 0;
          grapher.deleteAll(false);
          addNestedSubtree(0, 2, grapher);
          grapher.reDraw();
          linksModeBox.setEnabled(true);
        } else {
          minpos = 0;
          grapher.deleteAll(false);
          addSubtree(0, 4, grapher);
          grapher.reDraw();
          intergraphLinks = false;
          treeExtIntergraphLinks = false;
          linksModeBox.setSelectedIndex(0);
          linksModeBox.setEnabled(false);
        }
      }
    });
    options.add(nestedCheckBox);
    options.add(linksModeBox);
  }

  /**
   * Adds an object to the grapher.
   * It also adds the connection pin to the node.
   */
  void addNode(IlvGraphic node) {
    grapher.addNode(node, 1, false);
  }

  public void start() {
    // Ensures that the root object is in the middle.
    mgrview.translate(10, -grapher.boundingBox().height / 2 + getHeight() / 2, true);
  }

  /**
   * Generates a subtree and layout it. If ilog.views.graphlayout package
   * is available, this can advantageously be replaced by using a 
   * <code>ilog.views.graphlayout.tree.IlvTreeLayout</code>.
   * @param actlevel the actual level
   * @param maxlevel the maximal level
   * @param grapher the grapher
   * @return The root of the subtree.
   */
  private IlvGraphic addSubtree(int actlevel, int maxlevel, IlvGrapher grapher) {
    Color color = new Color(Math.min(50 + actlevel * 40, 255), Math.min(50 + actlevel * 40, 255),
        Math.min(100 + actlevel * 40, 255));

    IlvGraphic node = createNode(color);
    addNode(node);

    IlvRect rect;
    IlvGraphic child;
    IlvLinkImage link;
    double minChildPos = Double.MAX_VALUE;
    double maxChildPos = -Double.MAX_VALUE;

    if (actlevel < maxlevel) {
      for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
        child = addSubtree(actlevel + 1, maxlevel, grapher);
        rect = child.boundingBox();
        minpos = Math.max(minpos, rect.y + rect.height + MIN_YSPACE);
        minChildPos = Math.min(minChildPos, rect.y);
        maxChildPos = Math.max(maxChildPos, rect.y + rect.height);
        link = new IlvPolylineLinkImage(node, child, true, null);
        link.setForeground(color);
        link.setLineWidth(2);
        grapher.addLink(link, 0, false);
      }
    }

    if (minChildPos < maxChildPos)
      grapher.moveObject(node, actlevel * (NODE_WIDTH + MIN_XSPACE),
          0.5d * (maxChildPos + minChildPos - NODE_HEIGHT), false);
    else
      grapher.moveObject(node, actlevel * (NODE_WIDTH + MIN_XSPACE), minpos, false);

    return node;
  }

  private IlvGraphic addNestedSubtree(int actlevel, int maxlevel, IlvGrapher grapher) {
    Color color = new Color(Math.min(50 + actlevel * 40, 255), Math.min(50 + actlevel * 40, 255),
        Math.min(100 + actlevel * 40, 255));

    IlvLinkImage link;
    IlvGrapher subgraph = new IlvGrapher();
    IlvGraphic node1 = createNode(color);
    subgraph.addNode(node1, false);
    subgraph.moveObject(node1, 0, -0.5 * NODE_HEIGHT, false);

    IlvGraphic node2 = createNode(color);
    subgraph.addNode(node2, false);
    subgraph.moveObject(node2, NODE_WIDTH + MIN_XSPACE, -0.5 * NODE_HEIGHT, false);
    link = new IlvPolylineLinkImage(node1, node2, true, null);
    link.setForeground(color);
    link.setLineWidth(2);
    subgraph.addLink(link, 0, false);

    IlvGraphic node3 = null;
    if (actlevel < maxlevel) {
      node3 = createNode(color);
      subgraph.addNode(node3, false);
      link = new IlvPolylineLinkImage(node1, node3, true, null);
      link.setForeground(color);
      link.setLineWidth(2);
      subgraph.addLink(link, 0, false);
    }

    // add an invisible dummy object so that the subgraph stays at its
    // position even when all real nodes get invisible
    IlvGraphic dummy = new IlvRectangle(
        new IlvRect(0, -0.5 * NODE_HEIGHT, 2 * NODE_WIDTH + MIN_XSPACE, NODE_HEIGHT));
    dummy.setForeground(new Color(0, 0, 0, 0));
    dummy.setBackground(new Color(0, 0, 0, 0));
    subgraph.addObject(dummy, 0, false);

    // increase the margins of the manager frame
    IlvDefaultManagerFrame frame = (IlvDefaultManagerFrame) subgraph.getFrame();
    frame.setLeftMargin(10);
    frame.setRightMargin(10);
    frame.setOpaque(true);
    frame.setBackground(Color.yellow);

    addNode(subgraph);

    IlvRect rect;
    IlvGraphic child;
    IlvGraphic childBag;
    double minChildPos = Double.MAX_VALUE;
    double maxChildPos = -Double.MAX_VALUE;
    double childHeight = 0;

    if (actlevel < maxlevel) {
      for (int i = 0; i < 4; i++) {
        child = addNestedSubtree(actlevel + 1, maxlevel, grapher);
        childBag = (IlvGraphic) child.getGraphicBag();
        rect = childBag.boundingBox();
        childHeight = rect.height;
        minpos = Math.max(minpos, rect.y + rect.height + MIN_YSPACE);
        minChildPos = Math.min(minChildPos, rect.y);
        maxChildPos = Math.max(maxChildPos, rect.y + rect.height);
        switch (i) {
          case 0:
            link = new IlvPolylineLinkImage(node2, child, true, null);
            break;
          case 1:
            link = new IlvPolylineLinkImage(subgraph, child, true, null);
            break;
          case 2:
            link = new IlvPolylineLinkImage(subgraph, childBag, true, null);
            break;
          case 3:
            link = new IlvPolylineLinkImage(node3, childBag, true, null);
            break;
        }
        link.setForeground(color);
        link.setLineWidth(2);
        grapher.addLink(link, 2, false);
      }
    }

    if (minChildPos < maxChildPos) {
      double dist = (maxChildPos - minChildPos - childHeight) / 2;
      subgraph.moveObject(node2, NODE_WIDTH + MIN_XSPACE, -dist - 0.5 * NODE_HEIGHT, false);
      subgraph.moveObject(node3, NODE_WIDTH + MIN_XSPACE, dist - 0.5 * NODE_HEIGHT, false);
      subgraph.reshapeObject(dummy, new IlvRect(0, -dist - 0.5 * NODE_HEIGHT,
          2 * NODE_WIDTH + MIN_XSPACE, 2 * dist + NODE_HEIGHT), false);
      double height = subgraph.boundingBox().height;
      grapher.moveObject(subgraph, 2 * actlevel * (NODE_WIDTH + MIN_XSPACE),
          0.5 * (maxChildPos + minChildPos - height), false);
    } else {
      grapher.moveObject(subgraph, 2 * actlevel * (NODE_WIDTH + MIN_XSPACE), minpos, false);
    }

    return node1;
  }

  private IlvGraphic createNode(Color color) {
    final IlvCompositeGraphic node = new IlvCompositeGraphic();
    // Sets the layout
    IlvAttachmentLayout layout = new IlvAttachmentLayout();
    node.setLayout(layout);

    // Creates a white rectangle
    RoundRectangle2D.Double shape =
        new RoundRectangle2D.Double(0, 0, NODE_WIDTH, NODE_HEIGHT, 10, 10);
    IlvGeneralPath path = new IlvGeneralPath(shape);
    path.setFillOn(true);
    path.setStroke(new BasicStroke(2));
    path.setForeground(Color.black);
    path.setBackground(Color.white);
    node.setChildren(0, path);

    // Creates the container for stacking the icons horizontally to the right
    IlvCompositeGraphic iconsTop = new IlvCompositeGraphic();
    IlvStackerLayout stackerTop =
        new IlvStackerLayout(SwingConstants.RIGHT, SwingConstants.CENTER, 2);
    iconsTop.setLayout(stackerTop);

    IlvCompositeGraphic iconsBottom = new IlvCompositeGraphic();
    IlvStackerLayout stackerBottom =
        new IlvStackerLayout(SwingConstants.RIGHT, SwingConstants.CENTER, 2);
    iconsBottom.setLayout(stackerBottom);

    // Create the decoration buttons
    IlvGraphic showOrig = createShowHideButtonDeco(true, true);
    iconsTop.setChildren(2, showOrig);

    IlvGraphic hideOrig = createShowHideButtonDeco(true, false);
    iconsBottom.setChildren(2, hideOrig);

    IlvGraphic showDest = createShowHideButtonDeco(false, true);
    iconsTop.setChildren(1, showDest);

    IlvGraphic hideDest = createShowHideButtonDeco(false, false);
    iconsBottom.setChildren(1, hideDest);

    // Attach the TopCenter (hotspot) of the stacker to the
    // TopCenter (anchor) of the rectangle
    node.setChildren(1, iconsTop);
    node.setConstraints(1, new IlvAttachmentConstraint(IlvAttachmentLocation.TopCenter,
        IlvAttachmentLocation.TopCenter, 0, +1));

    // Attach the BottomCenter (hotspot) of the stacker to the
    // BottomCenter (anchor) of the rectangle
    node.setChildren(2, iconsBottom);
    node.setConstraints(2, new IlvAttachmentConstraint(IlvAttachmentLocation.BottomCenter,
        IlvAttachmentLocation.BottomCenter, 0, -1));

    Action[] actionsShowOrig = {new AbstractAction() {
      Override
      public void actionPerformed(ActionEvent e) {
        grapher.setVisibleBranch(node, startLevel, endLevel, true, true, intergraphLinks,
            treeExtIntergraphLinks);
        grapher.reDraw();
      }
    },};
    Action[] actionsHideOrig = {new AbstractAction() {
      Override
      public void actionPerformed(ActionEvent e) {
        grapher.setVisibleBranch(node, startLevel, endLevel, false, true, intergraphLinks,
            treeExtIntergraphLinks);
        grapher.reDraw();
      }
    },};
    Action[] actionsShowDest = {new AbstractAction() {
      Override
      public void actionPerformed(ActionEvent e) {
        grapher.setVisibleBranch(node, startLevel, endLevel, true, false, intergraphLinks,
            treeExtIntergraphLinks);
        grapher.reDraw();
      }
    },};
    Action[] actionsHideDest = {new AbstractAction() {
      Override
      public void actionPerformed(ActionEvent e) {
        grapher.setVisibleBranch(node, startLevel, endLevel, false, false, intergraphLinks,
            treeExtIntergraphLinks);
        grapher.reDraw();
      }
    },};

    String[] events = {"BUTTON1_CLICKED",};

    // Show Origin
    IlvEventMap eventMapShowOrig = new IlvEventMap();
    eventMapShowOrig.setActions(actionsShowOrig);
    eventMapShowOrig.setEvents(events);
    iconsTop.setEventMaps(2, eventMapShowOrig);

    // Hide Origin
    IlvEventMap eventMapHideOrig = new IlvEventMap();
    eventMapHideOrig.setActions(actionsHideOrig);
    eventMapHideOrig.setEvents(events);
    iconsBottom.setEventMaps(2, eventMapHideOrig);

    // Show Destination
    IlvEventMap eventMapShowDest = new IlvEventMap();
    eventMapShowDest.setActions(actionsShowDest);
    eventMapShowDest.setEvents(events);
    iconsTop.setEventMaps(1, eventMapShowDest);

    // Hide Destination
    IlvEventMap eventMapHideDest = new IlvEventMap();
    eventMapHideDest.setActions(actionsHideDest);
    eventMapHideDest.setEvents(events);
    iconsBottom.setEventMaps(1, eventMapHideDest);

    IlvCompositeInteractor compInteractor = new IlvCompositeInteractor() {
      Override
      public IlvCompositeContext getCompositeContext(Object source) {
        return null;
      }
    };
    node.setObjectInteractor(compInteractor);

    return node;
  }

  /**
   * Creates the decoration of the composite object (the node of
   * the grapher) for the button which allows either to expand or to
   * collapse a branch in a given direction.
   */
  private IlvGraphic createShowHideButtonDeco(boolean origin, boolean show) {
    // "o" stands for "origin", "d" for destination
    // "+" stands for "expand", "-" for collapse
    String text = (origin ? "o" : "d") + (show ? "+" : "-");
    IlvText buttonDeco = new IlvText(new IlvPoint(), text);
    buttonDeco.setFillOn(true);
    Color fillColor = show ? Color.green : Color.red;
    fillColor = fillColor.brighter().brighter().brighter();
    buttonDeco.setFillPaint(fillColor);
    double width = buttonDeco.boundingBox().width;
    buttonDeco.setLeftMargin(Math.max(0, (20 - width) / 2));
    buttonDeco.setRightMargin(Math.max(0, (20 - width) / 2));
    return buttonDeco;
  }

  /**
   * A button class that automatically installs an interactor.
   */
  private class InteractorButton extends JToggleButton implements ItemListener {
    private IlvManagerViewInteractor interactor;
    private ShowHideBranch editor;

    public InteractorButton(String text, IlvManagerViewInteractor inter, String tooltip,
        ShowHideBranch editor) {
      super(text);
      this.editor = editor;
      this.interactor = inter;
      setToolTipText(tooltip);
      addItemListener(this);
    }

    IlvManagerViewInteractor getInteractor() {
      return interactor;
    }

    Override
    public void itemStateChanged(ItemEvent event) {
      if (isSelected()) {
        // Removes the previous interactor and installs a new one.
        editor.mgrview.popInteractor();
        editor.mgrview.pushInteractor(interactor);
      } else
        editor.mgrview.popInteractor();
    }
  }

  public static void main(String[] args) {
    // Sun recommends that to put the entire GUI initialization into the
    // AWT thread
    SwingUtilities.invokeLater(new Runnable() {
      Override
      public void run() {
        ShowHideBranch app = new ShowHideBranch();
        app.init();

        JFrame frame = new JFrame("Show-Hide Grapher Branch");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(700, 600);
        frame.getContentPane().add(app);
        frame.setVisible(true);
      }
    });
  }
}