content: /*
   * The MIT License (MIT)
   *
   * Copyright (c) 2011-2021 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  
  package juicebox.windowui;
  
  import java.awt.*;
  
  
  /**
   * Layout manager for HiC panels, based loosely on BorderLayout
   */
  public class HiCLayout implements LayoutManager2 {
      public static final String NORTH_WEST = "Northwest";
      private static final String NORTH = "North";
      private static final String SOUTH = "South";
      private static final String EAST = "East";
      private static final String WEST = "West";
      private static final String CENTER = "Center";
      private final int hgap = 0;
      private final int vgap = 0;
      private Component north;
      private Component west;
      private Component east;
      private Component south;
      private Component center;
      private Component northwest;
  
      /**
       * Adds the specified component to the layout, using the specified
       * constraint object.  For border layouts, the constraint must be
       * one of the following constants:  <code>NORTH</code>,
       * <code>SOUTH</code>, <code>EAST</code>,
       * <code>WEST</code>, or <code>CENTER</code>.
       * <p/>
       * Most applications do not call this method directly. This method
       * is called when a component is added to a container using the
       * <code>Container.add</code> method with the same argument types.
       *
       * @param comp        the component to be added.
       * @param constraints an object that specifies how and where
       *                    the component is added to the layout.
       * @throws IllegalArgumentException if the constraint object is not
       *                                  a string, or if it not one of the five specified
       *                                  constants.
       * @see java.awt.Container#add(java.awt.Component, java.lang.Object)
       * @since JDK1.1
       */
      public void addLayoutComponent(Component comp, Object constraints) {
          synchronized (comp.getTreeLock()) {
              if ((constraints == null) || (constraints instanceof String)) {
                  addLayoutComponent((String) constraints, comp);
              } else {
                  throw new IllegalArgumentException("cannot add to layout: constraint must be a string (or null)");
              }
          }
      }
  
      public void addLayoutComponent(String name, Component comp) {
          synchronized (comp.getTreeLock()) {
              /* Special case:  treat null the same as "Center". */
              if (name == null) {
                  name = "Center";
              }
  
              /* Assign the component to one of the known regions of the layout.
               */
              if (name.equalsIgnoreCase(CENTER)) {
                  center = comp;
              } else if (name.equalsIgnoreCase(NORTH)) {
                  north = comp;
              } else if (name.equalsIgnoreCase(SOUTH)) {
                  south = comp;
              } else if (name.equalsIgnoreCase(EAST)) {
                  east = comp;
              } else if (name.equalsIgnoreCase(WEST)) {
                  west = comp;
              } else if (name.equalsIgnoreCase(NORTH_WEST)) {
                  northwest = comp;
              } else {
                  throw new IllegalArgumentException("cannot add to layout: unknown constraint: " + name);
              }
          }
      }
  
      /**
       * Removes the specified component from this border layout. This
       * method is called when a container calls its <code>remove</code> or
       * <code>removeAll</code> methods. Most applications do not call this
       * method directly.
       *
       * @param comp the component to be removed.
       * @see java.awt.Container#remove(java.awt.Component)
       * @see java.awt.Container#removeAll()
       */
      public void removeLayoutComponent(Component comp) {
          synchronized (comp.getTreeLock()) {
              if (comp == center) {
                  center = null;
              } else if (comp == north) {
                  north = null;
              } else if (comp == south) {
                  south = null;
              } else if (comp == east) {
                  east = null;
              } else if (comp == west) {
                  west = null;
              } else if (comp == northwest) {
                  northwest = null;
              }
          }
      }
  
  
      /**
       * Determines the minimum size of the <code>target</code> container
       * using this layout manager.
       * <p/>
       * This method is called when a container calls its
       * <code>getMinimumSize</code> method. Most applications do not call
       * this method directly.
       *
       * @param target the container in which to do the layout.
       * @return the minimum dimensions needed to lay out the subcomponents
       * of the specified container.
       * @see java.awt.Container
       * @see java.awt.BorderLayout#preferredLayoutSize
       * @see java.awt.Container#getMinimumSize()
       */
      public Dimension minimumLayoutSize(Container target) {
          synchronized (target.getTreeLock()) {
              Dimension dim = new Dimension(0, 0);
  
              boolean ltr = target.getComponentOrientation().isLeftToRight();
              Component c;
  
              if ((c = getChild(EAST, ltr)) != null) {
                  Dimension d = c.getMinimumSize();
                  dim.width += d.width + hgap;
                  dim.height = Math.max(d.height, dim.height);
              }
              if ((c = getChild(WEST, ltr)) != null) {
                  Dimension d = c.getMinimumSize();
                  dim.width += d.width + hgap;
                  dim.height = Math.max(d.height, dim.height);
              }
              if ((c = getChild(CENTER, ltr)) != null) {
                  Dimension d = c.getMinimumSize();
                  dim.width += d.width;
                  dim.height = Math.max(d.height, dim.height);
              }
              if ((c = getChild(NORTH, ltr)) != null) {
                  Dimension d = c.getMinimumSize();
                  dim.width = Math.max(d.width, dim.width);
                  dim.height += d.height + vgap;
              }
              if ((c = getChild(SOUTH, ltr)) != null) {
                  Dimension d = c.getMinimumSize();
                  dim.width = Math.max(d.width, dim.width);
                  dim.height += d.height + vgap;
              }
  
              Insets insets = target.getInsets();
              dim.width += insets.left + insets.right;
              dim.height += insets.top + insets.bottom;
  
              return dim;
          }
      }
  
      /**
       * Determines the preferred size of the <code>target</code>
       * container using this layout manager, based on the components
       * in the container.
       * <p/>
       * Most applications do not call this method directly. This method
       * is called when a container calls its <code>getPreferredSize</code>
       * method.
       *
       * @param target the container in which to do the layout.
       * @return the preferred dimensions to lay out the subcomponents
       * of the specified container.
       * @see java.awt.Container
       * @see java.awt.BorderLayout#minimumLayoutSize
       * @see java.awt.Container#getPreferredSize()
       */
      public Dimension preferredLayoutSize(Container target) {
          synchronized (target.getTreeLock()) {
              Dimension dim = new Dimension(0, 0);
  
              boolean ltr = target.getComponentOrientation().isLeftToRight();
              Component c;
  
              if ((c = getChild(EAST, ltr)) != null) {
                  Dimension d = c.getPreferredSize();
                  dim.width += d.width + hgap;
                  dim.height = Math.max(d.height, dim.height);
              }
              if ((c = getChild(WEST, ltr)) != null) {
                  Dimension d = c.getPreferredSize();
                  dim.width += d.width + hgap;
                  dim.height = Math.max(d.height, dim.height);
              }
              if ((c = getChild(CENTER, ltr)) != null) {
                  Dimension d = c.getPreferredSize();
                  dim.width += d.width;
                  dim.height = Math.max(d.height, dim.height);
              }
              if ((c = getChild(NORTH, ltr)) != null) {
                  Dimension d = c.getPreferredSize();
                  dim.width = Math.max(d.width, dim.width);
                  dim.height += d.height + vgap;
              }
              if ((c = getChild(SOUTH, ltr)) != null) {
                  Dimension d = c.getPreferredSize();
                  dim.width = Math.max(d.width, dim.width);
                  dim.height += d.height + vgap;
              }
  
              Insets insets = target.getInsets();
              dim.width += insets.left + insets.right;
              dim.height += insets.top + insets.bottom;
  
              return dim;
          }
      }
  
      /**
       * Returns the maximum dimensions for this layout given the components
       * in the specified target container.
       *
       * @param target the component which needs to be laid out
       * @see Container
       * @see #minimumLayoutSize
       * @see #preferredLayoutSize
       */
      public Dimension maximumLayoutSize(Container target) {
          return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
      }
  
      /**
       * Returns the alignment along the x axis.  This specifies how
       * the component would like to be aligned relative to other
       * components.  The value should be a number between 0 and 1
       * where 0 represents alignment along the origin, 1 is aligned
       * the furthest away from the origin, 0.5 is centered, etc.
       */
      public float getLayoutAlignmentX(Container parent) {
          return 0.5f;
      }
  
      /**
       * Returns the alignment along the y axis.  This specifies how
       * the component would like to be aligned relative to other
       * components.  The value should be a number between 0 and 1
       * where 0 represents alignment along the origin, 1 is aligned
       * the furthest away from the origin, 0.5 is centered, etc.
       */
      public float getLayoutAlignmentY(Container parent) {
          return 0.5f;
      }
  
      /**
       * Invalidates the layout, indicating that if the layout manager
       * has cached information it should be discarded.
       */
      public void invalidateLayout(Container target) {
      }
  
      /**
       * Lays out the container argument using this border layout.
       * <p/>
       * This method actually reshapes the components in the specified
       * container in order to satisfy the constraints of this
       * <code>BorderLayout</code> object. The <code>NORTH</code>
       * and <code>SOUTH</code> components, if any, are placed at
       * the top and bottom of the container, respectively. The
       * <code>WEST</code> and <code>EAST</code> components are
       * then placed on the left and right, respectively. Finally,
       * the <code>CENTER</code> object is placed in any remaining
       * space in the middle.
       * <p/>
       * Most applications do not call this method directly. This method
       * is called when a container calls its <code>doLayout</code> method.
       *
       * @param target the container in which to do the layout.
       * @see java.awt.Container
       * @see java.awt.Container#doLayout()
       */
      public void layoutContainer(Container target) {
          synchronized (target.getTreeLock()) {
              Insets insets = target.getInsets();
              int top = insets.top;
              int bottom = target.getHeight() - insets.bottom;
              int left = insets.left;
              int right = target.getWidth() - insets.right;
  
              int dh = bottom - top;
              int dw = right - left;
  
  
              int dx = west == null ? 0 : west.getPreferredSize().width + hgap;
              int dy = north == null ? 0 : north.getPreferredSize().height + vgap;
              if (south != null) dy += south.getPreferredSize().height + vgap;
  
              // Center must be square
              //int centerDim = Math.min(dw - dx, dh - dy);
              int centerDimX = dw - dx;
              int centerDimY = dh - dy;
  
              if (north != null) {
                  Dimension d = north.getPreferredSize();
                  north.setBounds(left + dx, top, centerDimX, d.height);
                  if (northwest != null) {
                      northwest.setBounds(left, top, dx, d.height);
                  }
                  top += d.height + vgap;
              }
              if (west != null) {
                  Dimension d = west.getPreferredSize();
                  west.setBounds(left, top, d.width, centerDimY);
              }
              if (center != null) {
                  center.setBounds(left + west.getWidth() + hgap, top, centerDimX, centerDimY);
              }
              if (east != null) {
                  int xEast = left + west.getWidth() + hgap + centerDimX + hgap;
                  int wEast = Math.max(0, dw - xEast);
                  east.setBounds(xEast, top, wEast, centerDimY);
              }
              if (south != null) {
                  top += centerDimY + vgap;
                  int hSouth = Math.max(0, dh - top);
                  south.setBounds(left, top, dw, hSouth);
              }
  
  
          }
      }
  
      /**
       * Get the component that corresponds to the given constraint location
       *
       * @param key The desired absolute position,
       *            either NORTH, SOUTH, EAST, or WEST.
       * @param ltr Is the component line direction left-to-right?
       */
      private Component getChild(String key, boolean ltr) {
          Component result = null;
  
          if (key.equalsIgnoreCase(NORTH)) {
              result = north;
          } else if (key.equalsIgnoreCase(SOUTH)) {
              result = south;
          } else if (key.equalsIgnoreCase(WEST)) {
              result = west;
          } else if (key.equalsIgnoreCase(EAST)) {
              result = east;
          } else if (key.equalsIgnoreCase(CENTER)) {
              result = center;
          } else if (key.equalsIgnoreCase(NORTH_WEST)) {
              result = northwest;
          }
  
          if (result != null && !result.isVisible()) {
              result = null;
          }
          return result;
      }
  
      /**
       * Returns a string representation of the state of this border layout.
       *
       * @return a string representation of this border layout.
       */
      public String toString() {
          return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]";
      }
  }
  