blob: 7b0b6b1c677e76718d75efb387910018779d5034 [file] [log] [blame]
/*
* Copyright 2000-2009 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.uiDesigner.radComponents;
import com.intellij.openapi.project.Project;
import com.intellij.uiDesigner.UIFormXmlConstants;
import com.intellij.uiDesigner.XmlWriter;
import com.intellij.uiDesigner.compiler.GridBagConverter;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.Util;
import com.intellij.uiDesigner.designSurface.ComponentDropLocation;
import com.intellij.uiDesigner.designSurface.FirstComponentInsertLocation;
import com.intellij.uiDesigner.propertyInspector.Property;
import com.intellij.uiDesigner.propertyInspector.PropertyEditor;
import com.intellij.uiDesigner.propertyInspector.PropertyRenderer;
import com.intellij.uiDesigner.propertyInspector.editors.PrimitiveTypeEditor;
import com.intellij.uiDesigner.propertyInspector.properties.AbstractInsetsProperty;
import com.intellij.uiDesigner.propertyInspector.properties.AbstractIntProperty;
import com.intellij.uiDesigner.propertyInspector.properties.HorzAlignProperty;
import com.intellij.uiDesigner.propertyInspector.properties.VertAlignProperty;
import com.intellij.uiDesigner.propertyInspector.renderers.LabelPropertyRenderer;
import com.intellij.uiDesigner.snapShooter.SnapshotContext;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
/**
* @author yole
*/
public class RadGridBagLayoutManager extends RadAbstractGridLayoutManager {
private int myLastSnapshotRow = -1;
private int myLastSnapshotCol = -1;
private final int[] mySnapshotXMax = new int[512];
private final int[] mySnapshotYMax = new int[512];
@Override
public String getName() {
return UIFormXmlConstants.LAYOUT_GRIDBAG;
}
@Override
public LayoutManager createLayout() {
return new GridBagLayout();
}
@Override
public void changeContainerLayout(RadContainer container) throws IncorrectOperationException {
if (container.getLayoutManager().isGrid()) {
// preprocess: store weights in GridBagConstraints
RadAbstractGridLayoutManager grid = container.getGridLayoutManager();
for (RadComponent c : container.getComponents()) {
GridBagConstraints gbc = GridBagConverter.getGridBagConstraints(c);
if (grid.canCellGrow(container, false, c.getConstraints().getColumn())) {
gbc.weightx = 1.0;
}
if (grid.canCellGrow(container, true, c.getConstraints().getRow())) {
gbc.weighty = 1.0;
}
c.setCustomLayoutConstraints(gbc);
}
}
super.changeContainerLayout(container);
}
public void writeChildConstraints(final XmlWriter writer, final RadComponent child) {
writeGridConstraints(writer, child);
if (child.getCustomLayoutConstraints() instanceof GridBagConstraints) {
GridBagConstraints gbc = (GridBagConstraints)child.getCustomLayoutConstraints();
writer.startElement(UIFormXmlConstants.ELEMENT_GRIDBAG);
try {
if (!gbc.insets.equals(new Insets(0, 0, 0, 0))) {
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_TOP, gbc.insets.top);
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_LEFT, gbc.insets.left);
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_BOTTOM, gbc.insets.bottom);
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_RIGHT, gbc.insets.right);
}
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_WEIGHTX, gbc.weightx);
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_WEIGHTY, gbc.weighty);
if (gbc.ipadx != 0) {
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_IPADX, gbc.ipadx);
}
if (gbc.ipady != 0) {
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_IPADY, gbc.ipady);
}
}
finally {
writer.endElement();
}
}
}
@Override
public void addComponentToContainer(final RadContainer container, final RadComponent component, final int index) {
super.addComponentToContainer(container, component, index);
GridBagConstraints gbc = GridBagConverter.getGridBagConstraints(component);
component.setCustomLayoutConstraints(gbc);
container.getDelegee().add(component.getDelegee(), gbc, index);
}
@Override
public void refresh(RadContainer container) {
checkEmptyCells(container);
}
private static final int MINIMUM_GRID_SIZE = 15;
private static void checkEmptyCells(RadContainer container) {
JComponent jComponent = container.getDelegee();
GridBagLayout layout = (GridBagLayout)jComponent.getLayout();
Dimension oldSize = jComponent.getSize();
// clear cells
layout.columnWidths = null;
layout.rowHeights = null;
// calculate layout fields
jComponent.setSize(500, 400);
jComponent.doLayout();
jComponent.setSize(oldSize);
int[][] dimensions = layout.getLayoutDimensions();
// check empty columns
int[] columnWidths = dimensions[0];
boolean doLayoutColumns = false;
for (int i = 0; i < columnWidths.length; i++) {
if (columnWidths[i] == 0) {
columnWidths[i] = MINIMUM_GRID_SIZE;
doLayoutColumns = true;
}
}
// check empty rows
int[] rowHeights = dimensions[1];
boolean doLayoutRows = false;
for (int i = 0; i < rowHeights.length; i++) {
if (rowHeights[i] == 0) {
rowHeights[i] = MINIMUM_GRID_SIZE;
doLayoutRows = true;
}
}
// apply changes
if (doLayoutColumns) {
layout.columnWidths = columnWidths;
}
if (doLayoutRows) {
layout.rowHeights = rowHeights;
}
if (doLayoutColumns || doLayoutRows) {
jComponent.doLayout();
}
}
@Override
public Property[] getContainerProperties(final Project project) {
return Property.EMPTY_ARRAY;
}
@Override
public Property[] getComponentProperties(final Project project, final RadComponent component) {
return new Property[]{
new HorzAlignProperty(),
new VertAlignProperty(),
new ComponentInsetsProperty(),
new WeightProperty(true),
new WeightProperty(false),
new IPadProperty(true),
new IPadProperty(false)
};
}
private static GridBagLayout getGridBag(RadContainer container) {
return (GridBagLayout)container.getLayout();
}
@Override
public int getGridRowCount(RadContainer container) {
int[][] layoutDimensions = getGridBag(container).getLayoutDimensions();
return layoutDimensions[1].length;
}
@Override
public int getGridColumnCount(RadContainer container) {
int[][] layoutDimensions = getGridBag(container).getLayoutDimensions();
return layoutDimensions[0].length;
}
@Override
public int[] getHorizontalGridLines(RadContainer container) {
return getGridLines(container, 1, 1);
}
@Override
public int[] getVerticalGridLines(RadContainer container) {
return getGridLines(container, 0, 1);
}
@Override
public int[] getGridCellCoords(RadContainer container, boolean isRow) {
return getGridLines(container, isRow ? 1 : 0, 0);
}
@Override
public int[] getGridCellSizes(RadContainer container, boolean isRow) {
int[][] layoutDimensions = getGridBag(container).getLayoutDimensions();
return layoutDimensions[isRow ? 1 : 0];
}
private static int[] getGridLines(final RadContainer container, final int rowColIndex, final int delta) {
final GridBagLayout gridBag = getGridBag(container);
Point layoutOrigin = gridBag.getLayoutOrigin();
int[][] layoutDimensions = gridBag.getLayoutDimensions();
int[] result = new int[layoutDimensions[rowColIndex].length + delta];
if (result.length > 0) {
result[0] = (rowColIndex == 0) ? layoutOrigin.x : layoutOrigin.y;
for (int i = 1; i < result.length; i++) {
result[i] = result[i - 1] + layoutDimensions[rowColIndex][i - 1];
}
}
return result;
}
@NotNull
@Override
public ComponentDropLocation getDropLocation(@NotNull RadContainer container, @Nullable final Point location) {
if (getGridRowCount(container) == 0 && getGridColumnCount(container) == 0) {
return new FirstComponentInsertLocation(container, new Rectangle(0, 0, container.getWidth(), container.getHeight()), 0, 0);
}
return super.getDropLocation(container, location);
}
public void copyGridSection(final RadContainer source, final RadContainer destination, final Rectangle rc) {
destination.setLayout(new GridBagLayout());
}
@Override
protected void updateConstraints(final RadComponent component) {
GridBagLayout layout = (GridBagLayout)component.getParent().getLayout();
GridBagConstraints gbc = GridBagConverter.getGridBagConstraints(component);
layout.setConstraints(component.getDelegee(), gbc);
super.updateConstraints(component);
}
@Override
public boolean isGridDefinedByComponents() {
return true;
}
@Override
public boolean canResizeCells() {
return false;
}
@Override
public boolean canCellGrow(RadContainer container, boolean isRow, int i) {
GridBagLayout gridBag = getGridBag(container);
double[][] weights = gridBag.getLayoutWeights();
if (weights != null) {
double[] cellWeights = weights[isRow ? 1 : 0];
return i >= 0 && i < cellWeights.length && cellWeights[i] >= 0.1;
}
return false;
}
@Override
public void setChildDragging(RadComponent child, boolean dragging) {
// do nothing here - setting visible to false would cause exceptions
}
@Override
public void createSnapshotLayout(final SnapshotContext context,
final JComponent parent,
final RadContainer container,
final LayoutManager layout) {
container.setLayout(new GridBagLayout());
}
public static Dimension getGridBagSize(final JComponent parent) {
GridBagLayout gridBag = (GridBagLayout)parent.getLayout();
gridBag.layoutContainer(parent);
int[][] layoutDimensions = gridBag.getLayoutDimensions();
int rowCount = layoutDimensions[1].length;
int colCount = layoutDimensions[0].length;
// account for invisible components
for (Component component : parent.getComponents()) {
final GridBagConstraints constraints = gridBag.getConstraints(component);
colCount = Math.max(colCount, constraints.gridx + constraints.gridwidth);
rowCount = Math.max(rowCount, constraints.gridy + constraints.gridheight);
}
return new Dimension(colCount, rowCount);
}
@Override
public void addSnapshotComponent(final JComponent parent,
final JComponent child,
final RadContainer container,
final RadComponent component) {
Dimension gridBagSize = getGridBagSize(parent);
// logic copied from GridBagLayout.java
GridBagLayout gridBag = (GridBagLayout)parent.getLayout();
final GridBagConstraints constraints = gridBag.getConstraints(child);
int curX = constraints.gridx;
int curY = constraints.gridy;
int curWidth = constraints.gridwidth;
int curHeight = constraints.gridheight;
int px;
int py;
/* If x or y is negative, then use relative positioning: */
if (curX < 0 && curY < 0) {
if (myLastSnapshotRow >= 0) {
curY = myLastSnapshotRow;
}
else if (myLastSnapshotCol >= 0) {
curX = myLastSnapshotCol;
}
else {
curY = 0;
}
}
if (curX < 0) {
if (curHeight <= 0) {
curHeight += gridBagSize.height - curY;
if (curHeight < 1) {
curHeight = 1;
}
}
px = 0;
for (int i = curY; i < (curY + curHeight); i++) {
px = Math.max(px, mySnapshotXMax[i]);
}
curX = px - curX - 1;
if (curX < 0) {
curX = 0;
}
}
else if (curY < 0) {
if (curWidth <= 0) {
curWidth += gridBagSize.width - curX;
if (curWidth < 1) {
curWidth = 1;
}
}
py = 0;
for (int i = curX; i < (curX + curWidth); i++) {
py = Math.max(py, mySnapshotYMax[i]);
}
curY = py - curY - 1;
if (curY < 0) {
curY = 0;
}
}
if (curWidth <= 0) {
curWidth += gridBagSize.width - curX;
if (curWidth < 1) {
curWidth = 1;
}
}
if (curHeight <= 0) {
curHeight += gridBagSize.height - curY;
if (curHeight < 1) {
curHeight = 1;
}
}
/* Adjust xMax and yMax */
for (int i = curX; i < (curX + curWidth); i++) {
mySnapshotYMax[i] = curY + curHeight;
}
for (int i = curY; i < (curY + curHeight); i++) {
mySnapshotXMax[i] = curX + curWidth;
}
/* Make negative sizes start a new row/column */
if (constraints.gridheight == 0 && constraints.gridwidth == 0) {
myLastSnapshotRow = myLastSnapshotCol = -1;
}
if (constraints.gridheight == 0 && myLastSnapshotRow < 0) {
myLastSnapshotCol = curX + curWidth;
}
else if (constraints.gridwidth == 0 && myLastSnapshotCol < 0) {
myLastSnapshotRow = curY + curHeight;
}
component.getConstraints().setColumn(curX);
component.getConstraints().setRow(curY);
component.getConstraints().setColSpan(curWidth);
component.getConstraints().setRowSpan(curHeight);
if (constraints.weightx >= 1.0) {
component.getConstraints().setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
}
else {
component.getConstraints().setHSizePolicy(0);
}
if (constraints.weighty >= 1.0) {
component.getConstraints().setVSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
}
else {
component.getConstraints().setVSizePolicy(0);
}
if (constraints.insets.right == 0 && constraints.insets.top == 0 && constraints.insets.bottom == 0) {
component.getConstraints().setIndent(constraints.insets.left / Util.DEFAULT_INDENT);
}
component.getConstraints().setAnchor(convertAnchor(constraints));
component.getConstraints().setFill(convertFill(constraints));
component.setCustomLayoutConstraints(constraints.clone());
container.addComponent(component);
}
private static int convertAnchor(final GridBagConstraints gbc) {
switch (gbc.anchor) {
case GridBagConstraints.NORTHWEST:
return GridConstraints.ANCHOR_NORTHWEST;
case GridBagConstraints.NORTH:
return GridConstraints.ANCHOR_NORTH;
case GridBagConstraints.NORTHEAST:
return GridConstraints.ANCHOR_NORTHEAST;
case GridBagConstraints.EAST:
return GridConstraints.ANCHOR_EAST;
case GridBagConstraints.SOUTHEAST:
return GridConstraints.ANCHOR_SOUTHEAST;
case GridBagConstraints.SOUTH:
return GridConstraints.ANCHOR_SOUTH;
case GridBagConstraints.SOUTHWEST:
return GridConstraints.ANCHOR_SOUTHWEST;
default:
return GridConstraints.ANCHOR_WEST;
}
}
private static int convertFill(final GridBagConstraints gbc) {
switch (gbc.fill) {
case GridBagConstraints.HORIZONTAL:
return GridConstraints.FILL_HORIZONTAL;
case GridBagConstraints.VERTICAL:
return GridConstraints.FILL_VERTICAL;
case GridBagConstraints.BOTH:
return GridConstraints.FILL_BOTH;
default:
return GridConstraints.FILL_NONE;
}
}
private static class ComponentInsetsProperty extends AbstractInsetsProperty<RadComponent> {
public ComponentInsetsProperty() {
super(null, "Insets");
}
public Insets getValue(final RadComponent component) {
if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
final GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
return gbc.insets;
}
return new Insets(0, 0, 0, 0);
}
protected void setValueImpl(final RadComponent component, final Insets value) throws Exception {
if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
final GridBagConstraints cellConstraints = (GridBagConstraints)component.getCustomLayoutConstraints();
cellConstraints.insets = value;
GridBagLayout layout = (GridBagLayout)component.getParent().getLayout();
GridBagConstraints gbc = (GridBagConstraints)layout.getConstraints(component.getDelegee()).clone();
gbc.insets = value;
layout.setConstraints(component.getDelegee(), gbc);
}
}
@Override
public boolean isModified(final RadComponent component) {
return !getValue(component).equals(new Insets(0, 0, 0, 0));
}
@Override
public void resetValue(final RadComponent component) throws Exception {
setValue(component, new Insets(0, 0, 0, 0));
}
}
private static class WeightProperty extends Property<RadComponent, Double> {
private final boolean myIsWeightX;
private LabelPropertyRenderer<Double> myRenderer;
private PropertyEditor<Double> myEditor;
public WeightProperty(final boolean isWeightX) {
super(null, isWeightX ? "Weight X" : "Weight Y");
myIsWeightX = isWeightX;
}
public Double getValue(final RadComponent component) {
if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
return myIsWeightX ? gbc.weightx : gbc.weighty;
}
return 0.0;
}
protected void setValueImpl(final RadComponent component, final Double value) throws Exception {
if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
if (myIsWeightX) {
gbc.weightx = value.doubleValue();
}
else {
gbc.weighty = value.doubleValue();
}
((GridBagLayout)component.getParent().getLayout()).setConstraints(component.getDelegee(), gbc);
}
}
@NotNull
public PropertyRenderer<Double> getRenderer() {
if (myRenderer == null) {
myRenderer = new LabelPropertyRenderer<Double>();
}
return myRenderer;
}
public PropertyEditor<Double> getEditor() {
if (myEditor == null) {
myEditor = new PrimitiveTypeEditor<Double>(Double.class);
}
return myEditor;
}
@Override
public boolean isModified(final RadComponent component) {
return !(new Double(0.0).equals(getValue(component)));
}
@Override
public void resetValue(final RadComponent component) throws Exception {
setValue(component, 0.0);
}
}
private static class IPadProperty extends AbstractIntProperty<RadComponent> {
private final boolean myIsIpadX;
public IPadProperty(final boolean isIpadX) {
super(null, isIpadX ? "Ipad X" : "Ipad Y", 0);
myIsIpadX = isIpadX;
}
public Integer getValue(final RadComponent component) {
if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
return myIsIpadX ? gbc.ipadx : gbc.ipady;
}
return 0;
}
protected void setValueImpl(final RadComponent component, final Integer value) throws Exception {
if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
if (myIsIpadX) {
gbc.ipadx = value.intValue();
}
else {
gbc.ipady = value.intValue();
}
((GridBagLayout)component.getParent().getLayout()).setConstraints(component.getDelegee(), gbc);
}
}
}
}