/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ui.components.panels;
import javax.swing.SwingConstants;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;
import java.util.ArrayList;
/**
* This class is intended to lay out added components horizontally.
* It allows to add them into the LEFT, CENTER, or RIGHT group, which are aligned separately.
* Every group can contain any amount of components. The specified gap is added between components,
* and the double gap is added between groups of components.
* <p><b>NB!: this class must be modified together with the <code>VerticalLayout</code> class accordingly</b></p>
*
* @author Sergey.Malenkov
* @see VerticalLayout
*/
public final class HorizontalLayout implements LayoutManager2 {
public static final String LEFT = "LEFT";
public static final String RIGHT = "RIGHT";
public static final String CENTER = "CENTER";
private final ArrayList<Component> myLeft = new ArrayList<Component>();
private final ArrayList<Component> myRight = new ArrayList<Component>();
private final ArrayList<Component> myCenter = new ArrayList<Component>();
private final int myAlignment;
private final int myGap;
/**
* Creates a layout with the specified gap.
* All components will have preferred widths,
* but their heights will be set according to the container.
*
* @param gap horizontal gap between components
*/
public HorizontalLayout(int gap) {
myGap = gap;
myAlignment = -1;
}
/**
* Creates a layout with the specified gap and vertical alignment.
* All components will have preferred sizes.
*
* @param gap horizontal gap between components
* @param alignment vertical alignment for components
*
* @see SwingConstants#TOP
* @see SwingConstants#BOTTOM
* @see SwingConstants#CENTER
*/
public HorizontalLayout(int gap, int alignment) {
myGap = gap;
switch (alignment) {
case SwingConstants.TOP:
case SwingConstants.BOTTOM:
case SwingConstants.CENTER:
myAlignment = alignment;
break;
default:
throw new IllegalArgumentException("unsupported alignment: " + alignment);
}
}
@Override
public void addLayoutComponent(Component component, Object constraints) {
if ((constraints == null) || (constraints instanceof String)) {
addLayoutComponent((String)constraints, component);
}
else {
throw new IllegalArgumentException("unsupported constraints: " + constraints);
}
}
@Override
public Dimension maximumLayoutSize(Container target) {
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
@Override
public float getLayoutAlignmentX(Container target) {
return .5f;
}
@Override
public float getLayoutAlignmentY(Container target) {
return .5f;
}
@Override
public void invalidateLayout(Container target) {
}
@Override
public void addLayoutComponent(String name, Component component) {
synchronized (component.getTreeLock()) {
if (name == null || LEFT.equalsIgnoreCase(name)) {
myLeft.add(component);
}
else if (CENTER.equalsIgnoreCase(name)) {
myCenter.add(component);
}
else if (RIGHT.equalsIgnoreCase(name)) {
myRight.add(component);
}
else {
throw new IllegalArgumentException("unsupported name: " + name);
}
}
}
@Override
public void removeLayoutComponent(Component component) {
myLeft.remove(component);
myRight.remove(component);
myCenter.remove(component);
}
@Override
public Dimension preferredLayoutSize(Container container) {
return getPreferredSize(container, true);
}
@Override
public Dimension minimumLayoutSize(Container container) {
return getPreferredSize(container, false);
}
@Override
public void layoutContainer(Container container) {
synchronized (container.getTreeLock()) {
Dimension left = getPreferredSize(myLeft);
Dimension right = getPreferredSize(myRight);
Dimension center = getPreferredSize(myCenter);
Insets insets = container.getInsets();
int width = container.getWidth() - insets.left - insets.right;
int height = container.getHeight() - insets.top - insets.bottom;
int leftX = 0;
if (left != null) {
leftX = myGap + layout(myLeft, 0, height, insets);
}
int rightX = width;
if (right != null) {
rightX -= right.width;
}
if (rightX < leftX) {
rightX = leftX;
}
if (center != null) {
int centerX = (width - center.width) / 2;
if (centerX > leftX) {
int centerRightX = centerX + center.width + myGap + myGap;
if (centerRightX > rightX) {
centerX = rightX - center.width - myGap - myGap;
}
}
if (centerX < leftX) {
centerX = leftX;
}
centerX = myGap + layout(myCenter, centerX, height, insets);
if (rightX < centerX) {
rightX = centerX;
}
}
if (right != null) {
layout(myRight, rightX, height, insets);
}
}
}
private int layout(ArrayList<Component> list, int x, int height, Insets insets) {
for (Component component : list) {
if (component.isVisible()) {
Dimension size = component.getPreferredSize();
int y = 0;
if (myAlignment == -1) {
size.height = height;
}
else if (myAlignment != SwingConstants.TOP) {
y = height - size.height;
if (myAlignment == SwingConstants.CENTER) {
y /= 2;
}
}
component.setBounds(x + insets.left, y + insets.top, size.width, size.height);
x += size.width + myGap;
}
}
return x;
}
private static Dimension join(Dimension result, int gap, Dimension size) {
if (size == null) {
return result;
}
if (result == null) {
return new Dimension(size);
}
result.width += gap + size.width;
if (result.height < size.height) {
result.height = size.height;
}
return result;
}
private Dimension getPreferredSize(ArrayList<Component> list) {
Dimension result = null;
for (Component component : list) {
if (component.isVisible()) {
result = join(result, myGap, component.getPreferredSize());
}
}
return result;
}
private Dimension getPreferredSize(Container container, boolean aligned) {
synchronized (container.getTreeLock()) {
Dimension left = getPreferredSize(myLeft);
Dimension right = getPreferredSize(myRight);
Dimension center = getPreferredSize(myCenter);
Dimension result = join(join(join(null, myGap + myGap, left), myGap + myGap, center), myGap + myGap, right);
if (result == null) {
result = new Dimension();
}
else if (aligned && center != null) {
int leftWidth = left == null ? 0 : left.width;
int rightWidth = right == null ? 0 : right.width;
result.width += Math.abs(leftWidth - rightWidth);
}
Insets insets = container.getInsets();
result.width += insets.left + insets.right;
result.height += insets.top + insets.bottom;
return result;
}
}
}