/*
* This file is part of lanterna (http://code.google.com/p/lanterna/).
*
* lanterna is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2010-2012 Martin
*/
package com.googlecode.lanterna.gui.layout;
import com.googlecode.lanterna.gui.Component;
import com.googlecode.lanterna.terminal.TerminalPosition;
import com.googlecode.lanterna.terminal.TerminalSize;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Layout manager inspired by java.awt.BorderLayout
* @author Martin
*/
public class BorderLayout implements LayoutManager {
public static final LayoutParameter CENTER = new LayoutParameter("BorderLayout.CENTER");
public static final LayoutParameter LEFT = new LayoutParameter("BorderLayout.LEFT");
public static final LayoutParameter RIGHT = new LayoutParameter("BorderLayout.RIGHT");
public static final LayoutParameter TOP = new LayoutParameter("BorderLayout.TOP");
public static final LayoutParameter BOTTOM = new LayoutParameter("BorderLayout.BOTTOM");
private static final Set<LayoutParameter> BORDER_LAYOUT_POSITIONS =
Collections.unmodifiableSet(new HashSet<LayoutParameter>(Arrays.asList(
CENTER,
LEFT,
RIGHT,
TOP,
BOTTOM)));
private final Map<LayoutParameter, Component> components;
public BorderLayout() {
components = new IdentityHashMap<LayoutParameter, Component>(5);
}
@Override
public void addComponent(Component component, LayoutParameter... parameters) {
if(parameters.length == 0) {
parameters = new LayoutParameter[] { CENTER };
}
else if(parameters.length > 1) {
throw new IllegalArgumentException("Calling BorderLayout.addComponent with more than one "
+ "layout parameter");
}
else if(!BORDER_LAYOUT_POSITIONS.contains(parameters[0])) {
throw new IllegalArgumentException("Calling BorderLayout.addComponent layout parameter " +
parameters[0] + " is not allowed");
}
else if(component == null) {
throw new IllegalArgumentException("Calling BorderLayout.addComponent component parameter " +
"set to null is not allowed");
}
//Make sure we don't add the same component twice
removeComponent(component);
synchronized(components) {
components.put(parameters[0], component);
}
}
@Override
public void removeComponent(Component component) {
synchronized(components) {
for(LayoutParameter parameter: BORDER_LAYOUT_POSITIONS) {
if(components.get(parameter) == component) {
components.remove(parameter);
return;
}
}
}
}
@Override
public TerminalSize getPreferredSize() {
synchronized(components) {
int preferredHeight =
(components.containsKey(TOP) ? components.get(TOP).getPreferredSize().getRows() : 0)
+
Math.max(
components.containsKey(LEFT) ? components.get(LEFT).getPreferredSize().getRows() : 0,
Math.max(
components.containsKey(CENTER) ? components.get(CENTER).getPreferredSize().getRows() : 0,
components.containsKey(RIGHT) ? components.get(RIGHT).getPreferredSize().getRows() : 0))
+
(components.containsKey(BOTTOM) ? components.get(BOTTOM).getPreferredSize().getRows() : 0);
int preferredWidth =
Math.max(
(components.containsKey(LEFT) ? components.get(LEFT).getPreferredSize().getColumns() : 0) +
(components.containsKey(CENTER) ? components.get(CENTER).getPreferredSize().getColumns() : 0) +
(components.containsKey(RIGHT) ? components.get(RIGHT).getPreferredSize().getColumns() : 0),
Math.max(
components.containsKey(TOP) ? components.get(TOP).getPreferredSize().getColumns() : 0,
components.containsKey(BOTTOM) ? components.get(BOTTOM).getPreferredSize().getColumns() : 0));
return new TerminalSize(preferredWidth, preferredHeight);
}
}
@Override
public List<? extends LaidOutComponent> layout(TerminalSize layoutArea) {
int availableHorizontalSpace = layoutArea.getColumns();
int availableVerticalSpace = layoutArea.getRows();
List<LaidOutComponent> finalLayout = new ArrayList<LaidOutComponent>();
synchronized(components) {
//We'll need this later on
int topComponentHeight = 0;
int leftComponentWidth = 0;
//First allocate the top
if(components.containsKey(TOP)) {
Component topComponent = components.get(TOP);
topComponentHeight = Math.min(topComponent.getPreferredSize().getRows(), availableVerticalSpace);
finalLayout.add(
new DefaultLaidOutComponent(
topComponent,
new TerminalSize(
availableHorizontalSpace,
topComponentHeight),
new TerminalPosition(0, 0)));
availableVerticalSpace -= topComponentHeight;
}
//Next allocate the bottom
if(components.containsKey(BOTTOM)) {
Component bottomComponent = components.get(BOTTOM);
int bottomComponentHeight = Math.min(bottomComponent.getPreferredSize().getRows(), availableVerticalSpace);
finalLayout.add(
new DefaultLaidOutComponent(
bottomComponent,
new TerminalSize(
availableHorizontalSpace,
bottomComponentHeight),
new TerminalPosition(0, layoutArea.getRows() - bottomComponentHeight)));
availableVerticalSpace -= bottomComponentHeight;
}
//Now divide the remaining space between LEFT, CENTER and RIGHT
if(components.containsKey(LEFT)) {
Component leftComponent = components.get(LEFT);
leftComponentWidth = Math.min(leftComponent.getPreferredSize().getColumns(), availableHorizontalSpace);
finalLayout.add(
new DefaultLaidOutComponent(
leftComponent,
new TerminalSize(
leftComponentWidth,
availableVerticalSpace),
new TerminalPosition(0, topComponentHeight)));
availableHorizontalSpace -= leftComponentWidth;
}
if(components.containsKey(RIGHT)) {
Component rightComponent = components.get(RIGHT);
int rightComponentWidth = Math.min(rightComponent.getPreferredSize().getColumns(), availableHorizontalSpace);
finalLayout.add(
new DefaultLaidOutComponent(
rightComponent,
new TerminalSize(
rightComponentWidth,
availableVerticalSpace),
new TerminalPosition(layoutArea.getColumns() - rightComponentWidth, topComponentHeight)));
availableHorizontalSpace -= rightComponentWidth;
}
if(components.containsKey(CENTER)) {
Component centerComponent = components.get(CENTER);
finalLayout.add(
new DefaultLaidOutComponent(
centerComponent,
new TerminalSize(
availableHorizontalSpace,
availableVerticalSpace),
new TerminalPosition(leftComponentWidth, topComponentHeight)));
}
}
return finalLayout;
}
@Override
public boolean maximisesVertically() {
return false;
}
@Override
public boolean maximisesHorisontally() {
return false;
}
}